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