Termlib: fixed endian bug while reading terminfo file
[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::InvalidTerminfoException::InvalidTerminfoException(std::string const & term)
138     : senf::Exception("Unreadable terminfo file")
139 {
140     if (!term.empty())
141         append( ": " + term);
142 }
143
144 prefix_ senf::term::Terminfo::Terminfo()
145 {}
146
147 prefix_ senf::term::Terminfo::Terminfo(std::string const & term)
148 {
149     load(term);
150 }
151
152 prefix_ void senf::term::Terminfo::load(std::string const & term)
153 {
154     std::string filename (findTerminfo(term));
155     if (filename.empty()) throw InvalidTerminfoException(term);
156     std::ifstream is (filename.c_str());
157     if (!is) throw InvalidTerminfoException(filename);
158     try {
159         load(is);
160     } catch (InvalidTerminfoException & ex) {
161         ex << ": " << filename;
162         throw ex;
163     }
164 }
165
166 prefix_ bool senf::term::Terminfo::getFlag(properties::Boolean p)
167     const
168 {
169     if (BoolVec::size_type(p) >= booleans_.size())
170         return false;
171     return booleans_[p];
172 }
173
174 prefix_ senf::term::Terminfo::number_t senf::term::Terminfo::getNumber(properties::Numeric p)
175     const
176 {
177     if (NumberVec::size_type(p) >= numbers_.size())
178         return NoValue;
179     return numbers_[p];
180 }
181
182 prefix_ senf::term::Terminfo::string_t senf::term::Terminfo::getString(properties::String p)
183     const
184 {
185     if (StringVec::size_type(p) >= strings_.size())
186         return 0;
187     return strings_[p];
188 }
189
190 prefix_ bool senf::term::Terminfo::hasProperty(properties::Boolean p)
191     const
192 {
193     return getFlag(p);
194 }
195
196 prefix_ bool senf::term::Terminfo::hasProperty(properties::Numeric p)
197     const
198 {
199     return getNumber(p) != NoValue;
200 }
201
202 prefix_ bool senf::term::Terminfo::hasProperty(properties::String p)
203     const
204 {
205     return getString(p) != 0;
206 }
207
208 namespace {
209
210     struct Stack
211     {
212         std::vector<senf::term::Terminfo::number_t> stack;
213
214         void push(senf::term::Terminfo::number_t v)
215             {
216                 stack.push_back(v);
217             }
218
219         senf::term::Terminfo::number_t pop()
220             {
221                 if (stack.empty())
222                     return 0;
223                 else {
224                     senf::term::Terminfo::number_t v (stack.back());
225                     stack.pop_back();
226                     return v;
227                 }
228             }
229
230         senf::term::Terminfo::number_t popNonzero()
231             {
232                 senf::term::Terminfo::number_t v (pop());
233                 return v ? v : 1;
234             }
235     };
236
237 }
238
239 // The following code is taken directly from utio. As far as I understand it is buggy
240 // and/or only partially implements the string format language. But seems to be enough for
241 // all the common terminal types ...
242 prefix_ std::string senf::term::Terminfo::formatString(properties::String p,
243                                                        number_t arg1, number_t arg2,
244                                                        number_t arg3, number_t arg4,
245                                                        number_t arg5, number_t arg6,
246                                                        number_t arg7, number_t arg8,
247                                                        number_t arg9)
248     const
249 {
250     char const * fmt_p (getString(p));
251     if (! fmt_p)
252         return "";
253
254     std::string const prgstr (fmt_p);
255     Stack stack;
256     bool bCondValue (false);
257     std::string result;
258
259     for (std::string::const_iterator i (prgstr.begin()); i != prgstr.end(); ++i) {
260     if (*i != '%') {
261         result += *i;
262         continue;
263     }
264     int width = 0, base = 0;
265     switch (*++i) {
266         case '%': result += *i; break;
267         case 'i': ++arg1; ++arg2; break;
268         case 'c': result += char(stack.pop());  break;
269         case 'x': base = 16; continue;
270         case '0': if (!base) base = 8;
271         case '1': case '2': case '3': case '4':
272         case '5': case '6': case '7': case '8':
273         case '9': if (!base) base = 10;
274             width = width * base + (*i - '0');
275             continue;
276         case '\\': base = 0;
277         case '{': continue;
278         case '\'': if (*(i - 1) == '%') {
279             if (*(i + 1) != '\\')
280                 width = *++i;
281             continue;
282         }
283         case '}': stack.push(width); break;
284         // Binary operands are in infix (reversed) order
285         case '+': stack.push(stack.pop() + stack.pop()); break;
286         case '-': stack.push(-stack.pop() + stack.pop()); break;
287         case '*': stack.push(stack.pop() * stack.pop()); break;
288         case '/': stack.push(stack.pop() / stack.popNonzero()); break;
289         case 'm': stack.push(stack.pop() % stack.popNonzero()); break;
290         case '|': stack.push(stack.pop() | stack.pop()); break;
291         case '&': stack.push(stack.pop() & stack.pop()); break;
292         case '^': stack.push(stack.pop() ^ stack.pop()); break;
293         case '>': stack.push(stack.pop() < stack.pop()); break;
294         case '<': stack.push(stack.pop() > stack.pop()); break;
295         case '=': stack.push(stack.pop() == stack.pop()); break;
296         case 'A': stack.push(stack.pop() && stack.pop()); break;
297         case 'O': stack.push(stack.pop() || stack.pop()); break;
298         case '!': stack.push(!stack.pop()); break;
299         case '~': stack.push(~stack.pop()); break;
300         case 't': bCondValue = stack.pop();
301         case 'e': if ((bCondValue = !bCondValue)) // this also supports elsif
302             --(i = prgstr.begin() + std::min (prgstr.find ("%e", i-prgstr.begin()),
303                                               prgstr.find ("%;", i-prgstr.begin())));
304         case '?':
305         case ';': break;
306         case 'p':
307             switch (*++i) {
308             case '1': stack.push(arg1); break;
309             case '2': stack.push(arg2); break;
310             case '3': stack.push(arg3); break;
311             case '4': stack.push(arg4); break;
312             case '5': stack.push(arg5); break;
313             case '6': stack.push(arg6); break;
314             case '7': stack.push(arg7); break;
315             case '8': stack.push(arg8); break;
316             case '9': stack.push(arg9); break;
317             }
318             break;
319         case 'd': {
320             number_t n = stack.pop();
321             const std::string::size_type iSize = result.size();
322             do {
323                 result += std::string::value_type('0' + (n % 10));
324             } while ((n /= 10) || --width > 0);
325             reverse (result.begin() + iSize, result.end());
326             break; }
327         }
328     }
329
330     return result;
331 }
332
333  prefix_ void senf::term::Terminfo::dump(std::ostream & os)
334      const
335  {
336      os << "Terminfo entry: " << name_ << "\n";
337      os << "Booleans: " << booleans_.size() << "\n";
338      os << "Numbers: " << numbers_.size() << "\n";
339      os << "Strings: " << strings_.size() << "\n";
340      os << "String pool size: " << stringPool_.size() << "\n";
341
342      {
343          os << "Flags:\n";
344          unsigned n (0);
345          BoolVec::const_iterator i (booleans_.begin());
346          BoolVec::const_iterator const i_end (booleans_.end());
347          for (; i != i_end; ++i, ++n)
348              if (*i && n < sizeof(properties::BooleanNames)/sizeof(properties::BooleanNames[0]))
349                  os << "    " << properties::BooleanNames[n] << "\n";
350      }
351
352      {
353          os << "Numbers:\n";
354          unsigned n (0);
355          NumberVec::const_iterator i (numbers_.begin());
356          NumberVec::const_iterator const i_end (numbers_.end());
357          for (; i != i_end; ++i, ++n)
358              if (*i != NoValue
359                  && n < sizeof(properties::NumericNames)/sizeof(properties::NumericNames[0]))
360                  os << "    " << properties::NumericNames[n] << " = " << *i << "\n";
361      }
362
363      {
364          os << "Strings:\n";
365          unsigned n (0);
366          StringVec::const_iterator i (strings_.begin());
367          StringVec::const_iterator const i_end (strings_.end());
368          for (; i != i_end; ++i, ++n)
369              if (*i && n < sizeof(properties::StringNames)/sizeof(properties::StringNames[0])) {
370                  os << "    " << std::setw(32) << properties::StringNames[n] << " = ";
371                  hexdump(*i, *i + strlen(*i), os, 32);
372              }
373      }
374
375 }
376
377 prefix_ std::string senf::term::Terminfo::findTerminfo(std::string const & name)
378 {
379     if (name.empty()) return "";
380     boost::filesystem::path subdir (name.substr(0,1)); subdir /= name;
381     boost::filesystem::path tientry;
382
383     {
384         char const * tivar (::getenv("TERMINFO"));
385         if (tivar) {
386             tientry = boost::filesystem::path(tivar) / subdir;
387             if (boost::filesystem::exists(tientry)) return tientry.string();
388         }
389     }
390
391     tientry = boost::filesystem::path("/etc/terminfo") / subdir;
392     if (boost::filesystem::exists(tientry)) return tientry.string();
393
394     tientry = boost::filesystem::path("/lib/terminfo")  / subdir;
395     if (boost::filesystem::exists(tientry)) return tientry.string();
396
397     tientry = boost::filesystem::path("/usr/share/terminfo") / subdir;
398     if (boost::filesystem::exists(tientry)) return tientry.string();
399
400     return "";
401 }
402
403 namespace {
404
405     boost::uint16_t const TerminfoMagic = 0x011A;
406
407     struct TerminfoHeader {
408         boost::uint16_t magic;
409         boost::uint16_t namesSz;
410         boost::uint16_t nBooleans;
411         boost::uint16_t nNumbers;
412         boost::uint16_t nStrings;
413         boost::uint16_t stringPoolSz;
414
415         void letoh() {
416             magic = le16toh(magic);
417             namesSz = le16toh(namesSz);
418             nBooleans = le16toh(nBooleans);
419             nNumbers = le16toh(nNumbers);
420             nStrings = le16toh(nStrings);
421             stringPoolSz = le16toh(stringPoolSz);
422         }
423     };
424
425 }
426
427 prefix_ void senf::term::Terminfo::load(std::istream & is)
428 {
429     TerminfoHeader h;
430     is.read(static_cast<char*>(static_cast<void*>(&h)), sizeof(h));
431     h.letoh();
432     if (!is || h.magic != TerminfoMagic) throw InvalidTerminfoException(
433             "invalid magic number (") << h.magic << "!=" << TerminfoMagic << ")";
434
435     name_.resize(h.namesSz);
436     is.read(&(name_[0]), name_.size());
437     if (!is) throw InvalidTerminfoException();
438     if (name_.size() & 1)
439         is.ignore(1u);
440     {
441         std::string::size_type n (name_.find('\0'));
442         if (n != std::string::npos)
443             name_.erase(n);
444     }
445
446     booleans_.resize(h.nBooleans);
447     for (BoolVec::iterator i (booleans_.begin()); i != booleans_.end(); ++i) {
448         char v;
449         is.read(&v, sizeof(v));
450         if (!is) throw InvalidTerminfoException();
451         *i = v;
452     }
453     if (booleans_.size() & 1)
454         is.ignore(1u);
455
456     numbers_.resize(h.nNumbers);
457     for (NumberVec::iterator i (numbers_.begin()); i != numbers_.end(); ++i) {
458         number_t v;
459         is.read(static_cast<char*>(static_cast<void*>(&v)), sizeof(v));
460         if (!is) throw InvalidTerminfoException();
461         *i = v;
462     }
463
464     typedef std::vector<number_t> OffsetVec;
465     OffsetVec offsets;
466     offsets.resize (h.nStrings);
467     for (OffsetVec::iterator i (offsets.begin()); i != offsets.end(); ++i) {
468         number_t v;
469         is.read(static_cast<char*>(static_cast<void*>(&v)), sizeof(v));
470         if (!is) throw InvalidTerminfoException();
471         *i = le16toh(v);
472     }
473
474     stringPool_.resize(h.stringPoolSz);
475     is.read(&(stringPool_[0]), stringPool_.size());
476     if (!is) throw InvalidTerminfoException();
477
478     strings_.resize(offsets.size());
479     StringVec::iterator j (strings_.begin());
480     for (OffsetVec::iterator i (offsets.begin()); i != offsets.end(); ++i, ++j)
481         if (*i != NoValue && *i >= 0 && unsigned(*i) < stringPool_.size())
482             *j = &(stringPool_[0]) + *i;
483         else
484             *j = 0;
485 }
486
487 //-/////////////////////////////////////////////////////////////////////////////////////////////////
488 // senf::term::KeyParser
489
490 char const * const senf::term::KeyParser::KeyNames[] = {
491     "Esc", "Backspace", "Backtab", "Begin", "CATab", "CTab", "Cancel", "Center", "Clear",
492     "ClearToEOL", "ClearToEOS", "Close", "Command", "Copy", "Create", "Delete", "DeleteLine",
493     "Down", "DownLeft", "DownRight", "End", "Enter", "Exit", "F0", "F1", "F2", "F3", "F4", "F5",
494     "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18", "F19",
495     "F20", "F21", "F22", "F23", "F24", "F25", "F26", "F27", "F28", "F29", "F30", "F31", "F32",
496     "F33", "F34", "F35", "F36", "F37", "F38", "F39", "F40", "F41", "F42", "F43", "F44", "F45",
497     "F46", "F47", "F48", "F49", "F50", "F51", "F52", "F53", "F54", "F55", "F56", "F57", "F58",
498     "F59", "F60", "F61", "F62", "F63", "Find", "Help", "Home", "Insert", "InsertLine", "Left",
499     "Mark", "Message", "Mouse", "Move", "Next", "Open", "Options", "PageDown", "PageUp", "Previous",
500     "Print", "Redo", "Reference", "Refresh", "Replace", "Restart", "Resume", "Right", "Save",
501     "Select", "ShiftBegin", "ShiftCancel", "ShiftCommand", "ShiftCopy", "ShiftCreate",
502     "ShiftDelete", "ShiftDeleteLine", "ShiftEnd", "ShiftClearToEOL", "ShiftExit", "ShiftFind",
503     "ShiftHelp", "ShiftHome", "ShiftInsert", "ShiftLeft", "ShiftMessage", "ShiftMove", "ShiftNext",
504     "ShiftOptions", "ShiftPrevious", "ShiftPrint", "ShiftRedo", "ShiftReplace", "ShiftResume",
505     "ShiftRight", "ShiftSave", "ShiftSuspend", "ShiftTab", "ShiftUndo", "Suspend", "Undo", "Up",
506     "UpLeft", "UpRight" };
507
508 prefix_ senf::term::KeyParser::KeyParser()
509 {}
510
511 prefix_ senf::term::KeyParser::KeyParser(Terminfo const & ti)
512 {
513     load(ti);
514 }
515
516 prefix_ void senf::term::KeyParser::load(Terminfo const & ti)
517 {
518     static Terminfo::properties::String keyStrings [] = {
519         Terminfo::properties::KeyCommand, Terminfo::properties::KeyBackspace,
520         Terminfo::properties::KeyBtab, Terminfo::properties::KeyBeg, Terminfo::properties::KeyCatab,
521         Terminfo::properties::KeyCtab, Terminfo::properties::KeyCancel, Terminfo::properties::KeyB2,
522         Terminfo::properties::KeyClear, Terminfo::properties::KeyEol, Terminfo::properties::KeyEos,
523         Terminfo::properties::KeyClose, Terminfo::properties::KeyCommand,
524         Terminfo::properties::KeyCopy, Terminfo::properties::KeyCreate, Terminfo::properties::KeyDc,
525         Terminfo::properties::KeyDl, Terminfo::properties::KeyDown, Terminfo::properties::KeyC1,
526         Terminfo::properties::KeyC3, Terminfo::properties::KeyEnd, Terminfo::properties::KeyEnter,
527         Terminfo::properties::KeyExit, Terminfo::properties::KeyF0, Terminfo::properties::KeyF1,
528         Terminfo::properties::KeyF2, Terminfo::properties::KeyF3, Terminfo::properties::KeyF4,
529         Terminfo::properties::KeyF5, Terminfo::properties::KeyF6, Terminfo::properties::KeyF7,
530         Terminfo::properties::KeyF8, Terminfo::properties::KeyF9, Terminfo::properties::KeyF10,
531         Terminfo::properties::KeyF11, Terminfo::properties::KeyF12, Terminfo::properties::KeyF13,
532         Terminfo::properties::KeyF14, Terminfo::properties::KeyF15, Terminfo::properties::KeyF16,
533         Terminfo::properties::KeyF17, Terminfo::properties::KeyF18, Terminfo::properties::KeyF19,
534         Terminfo::properties::KeyF20, Terminfo::properties::KeyF21, Terminfo::properties::KeyF22,
535         Terminfo::properties::KeyF23, Terminfo::properties::KeyF24, Terminfo::properties::KeyF25,
536         Terminfo::properties::KeyF26, Terminfo::properties::KeyF27, Terminfo::properties::KeyF28,
537         Terminfo::properties::KeyF29, Terminfo::properties::KeyF30, Terminfo::properties::KeyF31,
538         Terminfo::properties::KeyF32, Terminfo::properties::KeyF33, Terminfo::properties::KeyF34,
539         Terminfo::properties::KeyF35, Terminfo::properties::KeyF36, Terminfo::properties::KeyF37,
540         Terminfo::properties::KeyF38, Terminfo::properties::KeyF39, Terminfo::properties::KeyF40,
541         Terminfo::properties::KeyF41, Terminfo::properties::KeyF42, Terminfo::properties::KeyF43,
542         Terminfo::properties::KeyF44, Terminfo::properties::KeyF45, Terminfo::properties::KeyF46,
543         Terminfo::properties::KeyF47, Terminfo::properties::KeyF48, Terminfo::properties::KeyF49,
544         Terminfo::properties::KeyF50, Terminfo::properties::KeyF51, Terminfo::properties::KeyF52,
545         Terminfo::properties::KeyF53, Terminfo::properties::KeyF54, Terminfo::properties::KeyF55,
546         Terminfo::properties::KeyF56, Terminfo::properties::KeyF57, Terminfo::properties::KeyF58,
547         Terminfo::properties::KeyF59, Terminfo::properties::KeyF60, Terminfo::properties::KeyF61,
548         Terminfo::properties::KeyF62, Terminfo::properties::KeyF63, Terminfo::properties::KeyFind,
549         Terminfo::properties::KeyHelp, Terminfo::properties::KeyHome, Terminfo::properties::KeyIc,
550         Terminfo::properties::KeyIl, Terminfo::properties::KeyLeft, Terminfo::properties::KeyMark,
551         Terminfo::properties::KeyMessage, Terminfo::properties::KeyMouse,
552         Terminfo::properties::KeyMove, Terminfo::properties::KeyNext, Terminfo::properties::KeyOpen,
553         Terminfo::properties::KeyOptions, Terminfo::properties::KeyNpage,
554         Terminfo::properties::KeyPpage, Terminfo::properties::KeyPrevious,
555         Terminfo::properties::KeyPrint, Terminfo::properties::KeyRedo,
556         Terminfo::properties::KeyReference, Terminfo::properties::KeyRefresh,
557         Terminfo::properties::KeyReplace, Terminfo::properties::KeyRestart,
558         Terminfo::properties::KeyResume, Terminfo::properties::KeyRight,
559         Terminfo::properties::KeySave, Terminfo::properties::KeySelect,
560         Terminfo::properties::KeySbeg, Terminfo::properties::KeyScancel,
561         Terminfo::properties::KeyScommand, Terminfo::properties::KeyScopy,
562         Terminfo::properties::KeyScreate, Terminfo::properties::KeySdc,
563         Terminfo::properties::KeySdl, Terminfo::properties::KeySend, Terminfo::properties::KeySeol,
564         Terminfo::properties::KeySexit, Terminfo::properties::KeySfind,
565         Terminfo::properties::KeyShelp, Terminfo::properties::KeyShome,
566         Terminfo::properties::KeySic, Terminfo::properties::KeySleft,
567         Terminfo::properties::KeySmessage, Terminfo::properties::KeySmove,
568         Terminfo::properties::KeySnext, Terminfo::properties::KeySoptions,
569         Terminfo::properties::KeySprevious, Terminfo::properties::KeySprint,
570         Terminfo::properties::KeySredo, Terminfo::properties::KeySreplace,
571         Terminfo::properties::KeySrsume, Terminfo::properties::KeySright,
572         Terminfo::properties::KeySsave, Terminfo::properties::KeySsuspend,
573         Terminfo::properties::KeyStab, Terminfo::properties::KeySundo,
574         Terminfo::properties::KeySuspend, Terminfo::properties::KeyUndo,
575         Terminfo::properties::KeyUp, Terminfo::properties::KeyA1, Terminfo::properties::KeyA3 };
576
577     table_.clear();
578     for (unsigned i (0); i < sizeof(keyStrings)/sizeof(keyStrings[0]); ++i) {
579         char const * key (ti.getString(keyStrings[i]));
580         if (key)
581             table_.insert(std::make_pair(key, KeyCode(i+First)));
582     }
583 }
584
585 prefix_ std::pair<senf::term::KeyParser::keycode_t, std::string::size_type>
586 senf::term::KeyParser::lookup(std::string const & key)
587     const
588 {
589     if (key.empty())
590         return std::make_pair(KeyCode(0), 0);
591
592     // There are several cases:
593     // a) 'key' is an incomplete key sequence. In this case, 'key' will precede all completions in
594     //    the key table. The first possible completion is found by 'upper_bound'
595     // b) 'key' is a complete key sequence. This is the key sequence *preceding* the 'upper_bound'
596     // c) 'key' is a complete key sequence with additional trailing characters. In this case, 'key'
597     //    will follow the correct entry in the key table. Again, the correct key sequence is
598     //    the one preceding the 'upper_bound'
599
600     Keytable::const_iterator i (table_.upper_bound(key));
601     if (i != table_.end() && i->first.substr(0, key.size()) == key)
602         return std::make_pair(Incomplete, key.size());
603     if (i == table_.begin())
604         return std::make_pair(keycode_t(key[0]), 1);
605     --i;
606     if (key.substr(0, i->first.size()) == i->first)
607         return std::make_pair(i->second, i->first.size());
608     return std::make_pair(keycode_t(key[0]), 1);
609 }
610
611 prefix_ std::string senf::term::KeyParser::describe(keycode_t key)
612 {
613     if (key < keycode_t(' '))
614         return "^" + std::string(1, '@' + key);
615     if (key < 256)
616         return std::string(1, char(key));
617     if (key >= keycode_t(First) && key < keycode_t(First + sizeof(KeyNames) / sizeof(KeyNames[0])))
618         return std::string(KeyNames[key-First]);
619     else
620         return "<" + boost::lexical_cast<std::string>(unsigned(key)) + ">";
621 }
622
623 prefix_ void senf::term::KeyParser::dump(std::ostream & os)
624     const
625 {
626     os << "Keytable:\n";
627     for (Keytable::const_iterator i (table_.begin()); i != table_.end(); ++i) {
628         unsigned index (i->second - First);
629         if (index < sizeof(KeyNames)/sizeof(KeyNames[0])) {
630             std::cout << "    " << std::setw(32) << KeyNames[index] << ": ";
631             hexdump(i->first.begin(), i->first.end(), os);
632         }
633     }
634 }
635
636 //-/////////////////////////////////////////////////////////////////////////////////////////////////
637 #undef prefix_
638 //#include "Terminfo.mpp"
639
640 \f
641 // Local Variables:
642 // mode: c++
643 // fill-column: 100
644 // comment-column: 40
645 // c-file-style: "senf"
646 // indent-tabs-mode: nil
647 // ispell-local-dictionary: "american"
648 // compile-command: "scons -u test"
649 // End: