Utils/Termlib: Implement terminfo format string interpretation
g0dil [Tue, 6 Jan 2009 13:49:42 +0000 (13:49 +0000)]
Utils/Termlib: Decouple terminal and telnet dependent code using new AbstractTerminal API
Utils/Termlib: Implement basic terminal navigation commands

git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@1038 270642c3-0616-0410-b53a-bc976706d245

Utils/Termlib/AbstractTerminal.hh [new file with mode: 0644]
Utils/Termlib/Editor.cc [new file with mode: 0644]
Utils/Termlib/Editor.hh [new file with mode: 0644]
Utils/Termlib/TelnetTerminal.cc
Utils/Termlib/TelnetTerminal.hh
Utils/Termlib/Terminfo.cc
Utils/Termlib/Terminfo.hh
Utils/Termlib/Terminfo.test.cc
Utils/Termlib/telnetServer.cc
senfscons/senfutil.py

diff --git a/Utils/Termlib/AbstractTerminal.hh b/Utils/Termlib/AbstractTerminal.hh
new file mode 100644 (file)
index 0000000..01d72f7
--- /dev/null
@@ -0,0 +1,75 @@
+// $Id$
+//
+// Copyright (C) 2009 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief Terminal public header */
+
+#ifndef HH_SENF_Utils_Termlib_AbstractTerminal_
+#define HH_SENF_Utils_Termlib_AbstractTerminal_ 1
+
+// Custom includes
+#include <string>
+
+//#include "AbstractTerminal.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace senf {
+namespace term {
+
+    struct AbstractTerminal
+    {
+        struct Callbacks {
+            virtual ~Callbacks() {}
+            virtual void cb_init() = 0;
+            virtual void cb_charReceived(char ch) = 0;
+            virtual void cb_windowSizeChanged() = 0;
+        };
+
+        virtual ~AbstractTerminal() {}
+
+        virtual void setCallbacks(Callbacks & cb) = 0;
+
+        virtual std::string terminalType() = 0;
+        virtual unsigned width() = 0;
+        virtual unsigned height() = 0;
+
+        virtual void write(char ch) = 0;
+    };
+
+}}
+
+///////////////////////////////hh.e////////////////////////////////////////
+//#include "AbstractTerminal.cci"
+//#include "AbstractTerminal.ct"
+//#include "AbstractTerminal.cti"
+#endif
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
diff --git a/Utils/Termlib/Editor.cc b/Utils/Termlib/Editor.cc
new file mode 100644 (file)
index 0000000..c523907
--- /dev/null
@@ -0,0 +1,190 @@
+// $Id$
+//
+// Copyright (C) 2009 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief Editor non-inline non-template implementation */
+
+#include "Editor.hh"
+//#include "Editor.ih"
+
+// Custom includes
+#include <senf/Utils/membind.hh>
+#include <senf/Scheduler/Scheduler.hh>
+
+//#include "Editor.mpp"
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+prefix_ senf::term::BaseEditor::BaseEditor(AbstractTerminal & terminal)
+    : terminal_ (&terminal),
+      keyTimeout_ (senf::ClockService::milliseconds(DEFAULT_KEY_TIMEOUT_MS)),
+      timer_ ("senf::term::BaseEditor::keySequenceTimeout", 
+              senf::membind(&BaseEditor::keySequenceTimeout, this)),
+      column_ (0u)
+{
+    terminal_->setCallbacks(*this);
+}
+
+prefix_ void senf::term::BaseEditor::newline()
+{
+    write("\r\n");
+    write(tifo_.getString(Terminfo::properties::ClrEol));
+    column_ = 0;
+}
+
+prefix_ void senf::term::BaseEditor::toColumn(unsigned c)
+{
+    if (c >= width())
+        c = width();
+    if (c > column_) {
+        if (tifo_.hasProperty(Terminfo::properties::ParmRightCursor)) {
+            write(tifo_.formatString(Terminfo::properties::ParmRightCursor, c - column_));
+            column_ = c;
+        }
+        else {
+            char const * cuf1 (tifo_.getString(Terminfo::properties::CursorRight));
+            while (c > column_) {
+                write(cuf1);
+                ++column_;
+            }
+        }
+    }
+    else if (c < column_) {
+        if (tifo_.hasProperty(Terminfo::properties::ParmLeftCursor)) {
+            write(tifo_.formatString(Terminfo::properties::ParmLeftCursor, column_ - c));
+            column_ = c;
+        }
+        else {
+            char const * cub1 (tifo_.getString(Terminfo::properties::CursorRight));
+            while (c < column_) {
+                write(cub1);
+                --column_;
+            }
+        }
+    }
+}
+
+prefix_ void senf::term::BaseEditor::insertChar(char ch)
+{
+    if (column_+1 >= width())
+        return;
+    if (tifo_.hasProperty(Terminfo::properties::InsertCharacter))
+        write(tifo_.getString(Terminfo::properties::InsertCharacter));
+    else
+        write(tifo_.formatString(Terminfo::properties::ParmIch, 1));
+    write(ch);
+    ++ column_;
+}
+
+prefix_ void senf::term::BaseEditor::overwriteChar(char ch)
+{
+    write(ch);
+    ++ column_;
+}
+
+prefix_ void senf::term::BaseEditor::deleteChar()
+{
+    if (tifo_.hasProperty(Terminfo::properties::DeleteCharacter))
+        write(tifo_.getString(Terminfo::properties::DeleteCharacter));
+    else
+        write(tifo_.formatString(Terminfo::properties::ParmDch, 1));
+}
+
+prefix_ unsigned senf::term::BaseEditor::currentColumn()
+    const
+{
+    return column_;
+}
+
+prefix_ void senf::term::BaseEditor::cb_init()
+{
+    tifo_.load(terminal_->terminalType());
+    keyParser_.load(tifo_);
+    if (tifo_.hasProperty(Terminfo::properties::KeypadXmit))
+        write(tifo_.getString(Terminfo::properties::KeypadXmit));
+}
+
+prefix_ void senf::term::BaseEditor::cb_charReceived(char c)
+{
+    inputBuffer_ += c;
+    timer_.timeout(senf::scheduler::eventTime() + keyTimeout_);
+    processKeys();
+}
+
+prefix_ void senf::term::BaseEditor::cb_windowSizeChanged()
+{
+    if (column_ >= width())
+        column_ = width()-1;
+}
+
+prefix_ void senf::term::BaseEditor::keySequenceTimeout()
+{
+    while (!inputBuffer_.empty()) {
+        processKeys();
+        v_keyReceived(keycode_t(inputBuffer_[0]));
+        inputBuffer_.erase(0, 1);
+    }
+}
+
+prefix_ void senf::term::BaseEditor::processKeys()
+{
+    do {
+        std::pair<senf::term::KeyParser::keycode_t, std::string::size_type> result
+            (keyParser_.lookup(inputBuffer_));
+        if (result.first == senf::term::KeyParser::Incomplete)
+            return;
+        v_keyReceived(result.first);
+        inputBuffer_.erase(0, result.second);
+    } while (! inputBuffer_.empty());
+    timer_.disable();
+}
+
+prefix_ unsigned senf::term::BaseEditor::width()
+{
+    return terminal_->width();
+}
+
+prefix_ void senf::term::BaseEditor::write(char ch)
+{
+    terminal_->write(ch);
+}
+
+prefix_ void senf::term::BaseEditor::write(std::string const & s)
+{
+    for (std::string::const_iterator i (s.begin()); i != s.end(); ++i)
+        write(*i);
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+//#include "Editor.mpp"
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
diff --git a/Utils/Termlib/Editor.hh b/Utils/Termlib/Editor.hh
new file mode 100644 (file)
index 0000000..6253423
--- /dev/null
@@ -0,0 +1,99 @@
+// $Id$
+//
+// Copyright (C) 2009 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief Editor public header */
+
+#ifndef HH_SENF_Utils_Termlib_Editor_
+#define HH_SENF_Utils_Termlib_Editor_ 1
+
+// Custom includes
+#include <senf/Scheduler/ClockService.hh>
+#include <senf/Scheduler/TimerEvent.hh>
+#include "AbstractTerminal.hh"
+#include "Terminfo.hh"
+
+//#include "Editor.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace senf {
+namespace term {
+
+    class BaseEditor
+        : public AbstractTerminal::Callbacks
+    {
+    public:
+        typedef KeyParser::keycode_t keycode_t;
+
+        static unsigned const DEFAULT_KEY_TIMEOUT_MS = 500u;
+
+        explicit BaseEditor(AbstractTerminal & terminal);
+
+        void newline();                 ///< Move to beginning of a new, empty line
+        void toColumn(unsigned c);      ///< Move cursor to column \p c
+        void insertChar(char ch);       ///< Insert \p ch at current column, shifting text right
+        void overwriteChar(char ch);    ///< Write \p ch at current column
+        void deleteChar();              ///< Delete a character a current column
+
+        unsigned currentColumn() const; ///< Return number of current column
+
+    private:
+        virtual void v_keyReceived(keycode_t key) = 0;
+
+        virtual void cb_init();
+        virtual void cb_charReceived(char c);
+        virtual void cb_windowSizeChanged();
+
+        void keySequenceTimeout();
+        void processKeys();
+
+        unsigned width();
+        void write(char ch);
+        void write(std::string const & s);
+        
+        AbstractTerminal * terminal_;
+        Terminfo tifo_;
+        KeyParser keyParser_;
+        std::string inputBuffer_;
+        ClockService::clock_type keyTimeout_;
+        scheduler::TimerEvent timer_;
+        unsigned column_;
+    };
+
+}}
+
+///////////////////////////////hh.e////////////////////////////////////////
+//#include "Editor.cci"
+//#include "Editor.ct"
+//#include "Editor.cti"
+#endif
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
index 1c244ba..6b28a4f 100644 (file)
 //#include "TelnetTerminal.ih"
 
 // Custom includes
-#include <senf/Utils/membind.hh>
 
 //#include "TelnetTerminal.mpp"
 #define prefix_
 ///////////////////////////////cc.p////////////////////////////////////////
 
 prefix_ senf::term::TelnetTerminal::TelnetTerminal()
-    : keyTimeout_ (senf::ClockService::milliseconds(DEFAULT_KEY_TIMEOUT_MS)),
-      timer_ ("senf::term::TelnetTerminal::keySequenceTimeout", 
-              senf::membind(&TelnetTerminal::keySequenceTimeout, this))
 {
     requestPeerOption(telnetopt::SUPPRESS_GO_AHEAD);
     requestLocalOption(telnetopt::SUPPRESS_GO_AHEAD);
     requestLocalOption(telnetopt::ECHO);
 }
 
-prefix_ void senf::term::TelnetTerminal::v_setupComplete()
+prefix_ void senf::term::TelnetTerminal::setCallbacks(AbstractTerminal::Callbacks & cb)
+{
+    callbacks_ = &cb;
+}
+
+prefix_ std::string senf::term::TelnetTerminal::terminalType()
+{
+    return telnethandler::TerminalType::terminalType();
+}
+
+prefix_ unsigned senf::term::TelnetTerminal::width()
 {
-    tifo_.load(terminalType());
-    keyParser_.load(tifo_);
+    return telnethandler::NAWS::width();
 }
 
-prefix_ void senf::term::TelnetTerminal::v_charReceived(char c)
+prefix_ unsigned senf::term::TelnetTerminal::height()
+{
+    return telnethandler::NAWS::height();
+}
+
+prefix_ void senf::term::TelnetTerminal::write(char ch)
+{
+    BaseTelnetProtocol::write(ch);
+}
+
+prefix_ void senf::term::TelnetTerminal::v_setupComplete()
 {
-    inputBuffer_ += c;
-    timer_.timeout(senf::scheduler::eventTime() + keyTimeout_);
-    processKeys();
+    callbacks_->cb_init();
 }
 
-prefix_ void senf::term::TelnetTerminal::keySequenceTimeout()
+prefix_ void senf::term::TelnetTerminal::v_charReceived(char ch)
 {
-    while (!inputBuffer_.empty()) {
-        processKeys();
-        v_keyReceived(keycode_t(inputBuffer_[0]));
-        inputBuffer_.erase(0, 1);
-    }
+    callbacks_->cb_charReceived(ch);
 }
 
-prefix_ void senf::term::TelnetTerminal::processKeys()
+prefix_ void senf::term::TelnetTerminal::v_windowSizeChanged()
 {
-    do {
-        std::pair<senf::term::KeyParser::keycode_t, std::string::size_type> result
-            (keyParser_.lookup(inputBuffer_));
-        if (result.first == senf::term::KeyParser::Incomplete)
-            return;
-        v_keyReceived(result.first);
-        inputBuffer_.erase(0, result.second);
-    } while (! inputBuffer_.empty());
-    timer_.disable();
+    callbacks_->cb_windowSizeChanged();
 }
 
 ///////////////////////////////cc.e////////////////////////////////////////
index 0514c63..98cab31 100644 (file)
 #define HH_SENF_Utils_Termlib_TelnetTerminal_ 1
 
 // Custom includes
-#include <senf/Scheduler/TimerEvent.hh>
-#include <senf/Scheduler/ClockService.hh>
 #include "Telnet.hh"
-#include "Terminfo.hh"
+#include "AbstractTerminal.hh"
 
 //#include "TelnetTerminal.mpp"
 ///////////////////////////////hh.p////////////////////////////////////////
@@ -40,31 +38,26 @@ namespace term {
 
     class TelnetTerminal
         : public telnethandler::TerminalType,
-          public telnethandler::NAWS
+          public telnethandler::NAWS,
+          public AbstractTerminal
     {
     public:
-        typedef KeyParser::keycode_t keycode_t;
-
-        static unsigned const DEFAULT_KEY_TIMEOUT_MS = 500u;
-
         TelnetTerminal();
 
-    protected:
-        virtual void v_setupComplete();
+        virtual void setCallbacks(AbstractTerminal::Callbacks & cb);
+        virtual std::string terminalType();
+        virtual unsigned width();
+        virtual unsigned height();
+        virtual void write(char ch);
 
     private:
-        virtual void v_keyReceived(keycode_t key) = 0;
-
-        virtual void v_charReceived(char c);
-        void keySequenceTimeout();
-        void processKeys();
+        virtual void v_setupComplete();
+        virtual void v_charReceived(char ch);
+        virtual void v_windowSizeChanged();
 
-        senf::term::Terminfo tifo_;
-        senf::term::KeyParser keyParser_;
-        std::string inputBuffer_;
-        senf::ClockService::clock_type keyTimeout_;
-        senf::scheduler::TimerEvent timer_;
+        AbstractTerminal::Callbacks * callbacks_;
     };
+
 }}
 
 ///////////////////////////////hh.e////////////////////////////////////////
index 1a7c50f..7ad201f 100644 (file)
@@ -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
  {
index c34a568..f001df8 100644 (file)
@@ -47,15 +47,6 @@ namespace term {
         ///////////////////////////////////////////////////////////////////////////
         // Types
 
-        enum Color {
-            Black, Red, Green, Brown, Blue, Magenta, Cyan, LightGray, DarkGray, LightRed,
-            LightGreen, Yellow, LightBlue, Pink, LightCyan, White,
-            Preserve, LastColor = Preserve };
-
-        enum Attribute {
-            Standout, Underline, Reverse, Blink, HalfBright, Bold, Invisible, Protect, AltCharset,
-            Italic, Substript, Superscript, LastAttribute };
-
         enum { NoValue = -1 };
 
         struct properties
@@ -166,6 +157,17 @@ namespace term {
         bool getFlag(properties::Boolean p) const;
         number_t getNumber(properties::Numeric p) const;
         string_t getString(properties::String p) const;
+        bool hasProperty(properties::Boolean p) const;
+        bool hasProperty(properties::Numeric p ) const;
+        bool hasProperty(properties::String p ) const;
+        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;
+
+        ///////////////////////////////////////////////////////////////////////////
 
         void dump(std::ostream & os) const;
 
index 8ef55b3..37b7c2c 100644 (file)
@@ -74,6 +74,10 @@ BOOST_AUTO_UNIT_TEST(terminfo)
                        Pair(senf::term::KeyParser::keycode_t('b'), 1u) );
     BOOST_CHECK_EQUAL( kp.lookup(""),
                        Pair(senf::term::KeyParser::keycode_t('\0'), 0u) );
+
+    BOOST_CHECK_EQUAL( ifo.formatString(senf::term::Terminfo::properties::CursorAddress,
+                                        9, 12),
+                       "\e[10;13H" );
 }
 
 ///////////////////////////////cc.e////////////////////////////////////////
index da4c15a..a02711f 100644 (file)
 
 // Custom includes
 #include <boost/bind.hpp>
-#include "TelnetTerminal.hh"
 #include "../../Scheduler/Scheduler.hh"
 #include "../Logger.hh"
 #include "../../Socket/Protocols/INet.hh"
+#include "TelnetTerminal.hh"
+#include "Editor.hh"
 
 //#include "telnetServer.mpp"
 #define prefix_
 
 namespace {
 
-    class MyTelnet : public senf::term::TelnetTerminal
+    class MyEditor
+        : public senf::term::BaseEditor
     {
     public:
-        explicit MyTelnet(Handle handle) : senf::term::BaseTelnetProtocol(handle) {}
-        
+        MyEditor(senf::term::AbstractTerminal & terminal) 
+            : BaseEditor(terminal) {}
+
     private:
         virtual void v_keyReceived(keycode_t key)
             {
                 SENF_LOG(("Key " << senf::term::KeyParser::describe(key)));
+                if (key >= ' ' && key < 256)
+                    insertChar(key);
+                else
+                    switch (key) {
+                    case '\r':
+                        newline();
+                        break;
+                    case senf::term::KeyParser::Left: 
+                    {
+                        unsigned c (currentColumn());
+                        if (c > 0)
+                            toColumn(c-1);
+                        break;
+                    }
+                    case senf::term::KeyParser::Backspace: 
+                    {
+                        unsigned c (currentColumn());
+                        if (c > 0) {
+                            toColumn(c-1);
+                            deleteChar();
+                        }
+                        break;
+                    }
+                    case senf::term::KeyParser::Delete:
+                        deleteChar();
+                        break;
+                    }
             }
 
+    };
+
+    class MyTelnet 
+        : public senf::term::TelnetTerminal
+    {
+    public:
+        explicit MyTelnet(Handle handle) 
+            : senf::term::BaseTelnetProtocol(handle),
+              editor_(*this) {}
+        
         virtual void v_eof()
             {
                 SENF_LOG(("EOF"));
                 delete this;
             }
 
-        virtual void v_setupComplete()
-            {
-                TelnetTerminal::v_setupComplete();
-                SENF_LOG(("Terminal type is '" << terminalType() << "', window size is "
-                          << width() << "x" << height()));
-            }
-
-        virtual void v_windowSizeChanged()
-            {
-                SENF_LOG(("New window size: " << width() << "x" << height()));
-            }
+    private:
+        MyEditor editor_;
     };
 
     typedef senf::TCPv4ServerSocketHandle ServerHandle;
index af58a20..d9f67ed 100644 (file)
@@ -9,10 +9,12 @@ from SCons.Script import *
 
 def SetupForSENF(env):
     env.Append( LIBS           = [ 'senf', 'readline', 'rt', '$BOOSTREGEXLIB',
-                                   '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB' ],
+                                   '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB',
+                                   '$BOOSTFSLIB' ],
                 BOOSTREGEXLIB  = 'boost_regex',
                 BOOSTIOSTREAMSLIB = 'boost_iostreams',
                 BOOSTSIGNALSLIB = 'boost_signals',
+                BOOSTFSLIB = 'boost_filesystem',
                 CXXFLAGS       = [ '-Wno-long-long',
                                    '${"$CXXFLAGS_"+(final and "final" or "debug")}',
                                    '${profile and ("-g","-pg") or None}' ],