PPI: Add missing TargetDgramWriter doku
[senf.git] / Utils / Termlib / Terminfo.cc
index 1a7c50f..44bdb6f 100644 (file)
@@ -140,9 +140,9 @@ prefix_ senf::term::Terminfo::Terminfo(std::string const & term)
 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);
 }
 
@@ -170,6 +170,149 @@ prefix_ senf::term::Terminfo::string_t senf::term::Terminfo::getString(propertie
     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
  {
@@ -216,28 +359,25 @@ prefix_ senf::term::Terminfo::string_t senf::term::Terminfo::getString(propertie
 
 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 "";
@@ -262,11 +402,13 @@ prefix_ void senf::term::Terminfo::load(std::istream & is)
 {
     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)
@@ -277,6 +419,7 @@ prefix_ void senf::term::Terminfo::load(std::istream & is)
     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)
@@ -286,6 +429,7 @@ prefix_ void senf::term::Terminfo::load(std::istream & is)
     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;
     }
 
@@ -295,17 +439,21 @@ prefix_ void senf::term::Terminfo::load(std::istream & is)
     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;
 }
 
 ///////////////////////////////////////////////////////////////////////////