prefix_ void senf::term::Terminfo::load(std::string const & term)
{
std::string filename (findTerminfo(term));
+ if (filename.empty()) throw InvalidTerminfoException();
std::ifstream is (filename.c_str());
- if (!is)
- throw InvalidTerminfoException();
+ if (!is) throw InvalidTerminfoException();
load(is);
}
return strings_[p];
}
+prefix_ bool senf::term::Terminfo::hasProperty(properties::Boolean p)
+ const
+{
+ return getFlag(p);
+}
+
+prefix_ bool senf::term::Terminfo::hasProperty(properties::Numeric p)
+ const
+{
+ return getNumber(p) != NoValue;
+}
+
+prefix_ bool senf::term::Terminfo::hasProperty(properties::String p)
+ const
+{
+ return getString(p) != 0;
+}
+
+namespace {
+
+ struct Stack
+ {
+ std::vector<senf::term::Terminfo::number_t> stack;
+
+ void push(senf::term::Terminfo::number_t v)
+ {
+ stack.push_back(v);
+ }
+
+ senf::term::Terminfo::number_t pop()
+ {
+ if (stack.empty())
+ return 0;
+ else {
+ senf::term::Terminfo::number_t v (stack.back());
+ stack.pop_back();
+ return v;
+ }
+ }
+
+ senf::term::Terminfo::number_t popNonzero()
+ {
+ senf::term::Terminfo::number_t v (pop());
+ return v ? v : 1;
+ }
+ };
+
+}
+
+// The following code is taken directly from utio. As far as I understand it is buggy
+// and/or only partially implements the string format language. But seems to be enough for
+// all the common terminal types ...
+prefix_ std::string senf::term::Terminfo::formatString(properties::String p,
+ number_t arg1, number_t arg2,
+ number_t arg3, number_t arg4,
+ number_t arg5, number_t arg6,
+ number_t arg7, number_t arg8,
+ number_t arg9)
+ const
+{
+ char const * fmt_p (getString(p));
+ if (! fmt_p)
+ return "";
+
+ std::string const prgstr (fmt_p);
+ Stack stack;
+ bool bCondValue (false);
+ std::string result;
+
+ for (std::string::const_iterator i (prgstr.begin()); i != prgstr.end(); ++i) {
+ if (*i != '%') {
+ result += *i;
+ continue;
+ }
+ int width = 0, base = 0;
+ switch (*++i) {
+ case '%': result += *i; break;
+ case 'i': ++arg1; ++arg2; break;
+ case 'c': result += char(stack.pop()); break;
+ case 'x': base = 16; continue;
+ case '0': if (!base) base = 8;
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8':
+ case '9': if (!base) base = 10;
+ width = width * base + (*i - '0');
+ continue;
+ case '\\': base = 0;
+ case '{': continue;
+ case '\'': if (*(i - 1) == '%') {
+ if (*(i + 1) != '\\')
+ width = *++i;
+ continue;
+ }
+ case '}': stack.push(width); break;
+ // Binary operands are in infix (reversed) order
+ case '+': stack.push(stack.pop() + stack.pop()); break;
+ case '-': stack.push(-stack.pop() + stack.pop()); break;
+ case '*': stack.push(stack.pop() * stack.pop()); break;
+ case '/': stack.push(stack.pop() / stack.popNonzero()); break;
+ case 'm': stack.push(stack.pop() % stack.popNonzero()); break;
+ case '|': stack.push(stack.pop() | stack.pop()); break;
+ case '&': stack.push(stack.pop() & stack.pop()); break;
+ case '^': stack.push(stack.pop() ^ stack.pop()); break;
+ case '>': stack.push(stack.pop() < stack.pop()); break;
+ case '<': stack.push(stack.pop() > stack.pop()); break;
+ case '=': stack.push(stack.pop() == stack.pop()); break;
+ case 'A': stack.push(stack.pop() && stack.pop()); break;
+ case 'O': stack.push(stack.pop() || stack.pop()); break;
+ case '!': stack.push(!stack.pop()); break;
+ case '~': stack.push(~stack.pop()); break;
+ case 't': bCondValue = stack.pop();
+ case 'e': if ((bCondValue = !bCondValue)) // this also supports elsif
+ --(i = prgstr.begin() + std::min (prgstr.find ("%e", i-prgstr.begin()),
+ prgstr.find ("%;", i-prgstr.begin())));
+ case '?':
+ case ';': break;
+ case 'p':
+ switch (*++i) {
+ case '1': stack.push(arg1); break;
+ case '2': stack.push(arg2); break;
+ case '3': stack.push(arg3); break;
+ case '4': stack.push(arg4); break;
+ case '5': stack.push(arg5); break;
+ case '6': stack.push(arg6); break;
+ case '7': stack.push(arg7); break;
+ case '8': stack.push(arg8); break;
+ case '9': stack.push(arg9); break;
+ }
+ break;
+ case 'd': {
+ number_t n = stack.pop();
+ const std::string::size_type iSize = result.size();
+ do {
+ result += std::string::value_type('0' + (n % 10));
+ } while ((n /= 10) || --width > 0);
+ reverse (result.begin() + iSize, result.end());
+ break; }
+ }
+ }
+
+ return result;
+}
+
prefix_ void senf::term::Terminfo::dump(std::ostream & os)
const
{
prefix_ std::string senf::term::Terminfo::findTerminfo(std::string const & name)
{
+ if (name.empty()) return "";
boost::filesystem::path subdir (name.substr(0,1)); subdir /= name;
- boost::filesystem::path tientry, tipath;
+ boost::filesystem::path tientry;
{
char const * tivar (::getenv("TERMINFO"));
if (tivar) {
- tipath = tivar;
- tientry = tipath / subdir;
+ tientry = boost::filesystem::path(tivar) / subdir;
if (boost::filesystem::exists(tientry)) return tientry.native_file_string();
}
}
- tipath = "/etc/terminfo";
- tientry = tipath / subdir;
+ tientry = boost::filesystem::path("/etc/terminfo") / subdir;
if (boost::filesystem::exists(tientry)) return tientry.native_file_string();
- tipath = "/lib/terminfo";
- tientry = tipath / subdir;
+ tientry = boost::filesystem::path("/lib/terminfo") / subdir;
if (boost::filesystem::exists(tientry)) return tientry.native_file_string();
- tipath = "/usr/share/terminfo";
- tientry = tipath / subdir;
+ tientry = boost::filesystem::path("/usr/share/terminfo") / subdir;
if (boost::filesystem::exists(tientry)) return tientry.native_file_string();
return "";
{
TerminfoHeader h;
is.read(static_cast<char*>(static_cast<void*>(&h)), sizeof(h));
- if (h.magic != TerminfoMagic)
- throw InvalidTerminfoException();
+ if (!is || h.magic != TerminfoMagic) throw InvalidTerminfoException();
name_.resize(h.namesSz);
is.read(&(name_[0]), name_.size());
+ if (!is) throw InvalidTerminfoException();
+ if (name_.size() & 1)
+ is.ignore(1u);
{
std::string::size_type n (name_.find('\0'));
if (n != std::string::npos)
for (BoolVec::iterator i (booleans_.begin()); i != booleans_.end(); ++i) {
char v;
is.read(&v, sizeof(v));
+ if (!is) throw InvalidTerminfoException();
*i = v;
}
if (booleans_.size() & 1)
for (NumberVec::iterator i (numbers_.begin()); i != numbers_.end(); ++i) {
number_t v;
is.read(static_cast<char*>(static_cast<void*>(&v)), sizeof(v));
+ if (!is) throw InvalidTerminfoException();
*i = v;
}
for (OffsetVec::iterator i (offsets.begin()); i != offsets.end(); ++i) {
number_t v;
is.read(static_cast<char*>(static_cast<void*>(&v)), sizeof(v));
+ if (!is) throw InvalidTerminfoException();
*i = v;
}
stringPool_.resize(h.stringPoolSz);
is.read(&(stringPool_[0]), stringPool_.size());
+ if (!is) throw InvalidTerminfoException();
strings_.resize(offsets.size());
StringVec::iterator j (strings_.begin());
for (OffsetVec::iterator i (offsets.begin()); i != offsets.end(); ++i, ++j)
- if (*i != NoValue)
+ if (*i != NoValue && *i >= 0 && unsigned(*i) < stringPool_.size())
*j = &(stringPool_[0]) + *i;
+ else
+ *j = 0;
}
///////////////////////////////////////////////////////////////////////////