Utils/Termlib: Implement LineEditor
g0dil [Wed, 7 Jan 2009 13:13:14 +0000 (13:13 +0000)]
Utils/Termlib: Implement more terminfo error handling

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

Utils/Termlib/Editor.cc
Utils/Termlib/Editor.hh
Utils/Termlib/Terminfo.cc
Utils/Termlib/Terminfo.hh
Utils/Termlib/telnetServer.cc

index c523907..0023322 100644 (file)
@@ -74,7 +74,7 @@ prefix_ void senf::term::BaseEditor::toColumn(unsigned c)
             column_ = c;
         }
         else {
-            char const * cub1 (tifo_.getString(Terminfo::properties::CursorRight));
+            char const * cub1 (tifo_.getString(Terminfo::properties::CursorLeft));
             while (c < column_) {
                 write(cub1);
                 --column_;
@@ -83,34 +83,49 @@ prefix_ void senf::term::BaseEditor::toColumn(unsigned c)
     }
 }
 
-prefix_ void senf::term::BaseEditor::insertChar(char ch)
+prefix_ void senf::term::BaseEditor::put(char ch)
 {
-    if (column_+1 >= width())
+    if (column_ >= width()-1)
         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)
+prefix_ void senf::term::BaseEditor::put(std::string const & text)
 {
-    write(ch);
-    ++ column_;
+    if (text.size() > width()-column_-1) {
+        write(text.substr(0,width()-column_-1));
+        column_ = width() - 1;
+    }
+    else {
+        write(text);
+        column_ += text.size();
+    }
+}
+
+prefix_ void senf::term::BaseEditor::clearLine()
+{
+    write("\r");
+    write(tifo_.getString(Terminfo::properties::ClrEol));
+    column_ = 0;
+}
+
+prefix_ void senf::term::BaseEditor::setBold()
+{
+    if (tifo_.hasProperty(Terminfo::properties::EnterBoldMode) &&
+        tifo_.hasProperty(Terminfo::properties::ExitAttributeMode))
+        write(tifo_.getString(Terminfo::properties::EnterBoldMode));
 }
 
-prefix_ void senf::term::BaseEditor::deleteChar()
+prefix_ void senf::term::BaseEditor::setNormal()
 {
-    if (tifo_.hasProperty(Terminfo::properties::DeleteCharacter))
-        write(tifo_.getString(Terminfo::properties::DeleteCharacter));
-    else
-        write(tifo_.formatString(Terminfo::properties::ParmDch, 1));
+    if (tifo_.hasProperty(Terminfo::properties::EnterBoldMode) &&
+        tifo_.hasProperty(Terminfo::properties::ExitAttributeMode))
+        write(tifo_.getString(Terminfo::properties::ExitAttributeMode));
 }
 
 prefix_ unsigned senf::term::BaseEditor::currentColumn()
-    const
+   const
 {
     return column_;
 }
@@ -119,6 +134,13 @@ prefix_ void senf::term::BaseEditor::cb_init()
 {
     tifo_.load(terminal_->terminalType());
     keyParser_.load(tifo_);
+
+    typedef Terminfo::properties p;
+    if (! (tifo_.hasProperty(p::ClrEol) &&
+           (tifo_.hasProperty(p::ParmRightCursor) || tifo_.hasProperty(p::CursorRight)) &&
+           (tifo_.hasProperty(p::ParmLeftCursor) || tifo_.hasProperty(p::CursorLeft))))
+        throw Terminfo::InvalidTerminfoException();
+
     if (tifo_.hasProperty(Terminfo::properties::KeypadXmit))
         write(tifo_.getString(Terminfo::properties::KeypadXmit));
 }
@@ -174,6 +196,266 @@ prefix_ void senf::term::BaseEditor::write(std::string const & s)
         write(*i);
 }
 
+///////////////////////////////////////////////////////////////////////////
+
+prefix_ senf::term::LineEditor::LineEditor(AbstractTerminal & terminal, AcceptCallback cb)
+    : BaseEditor(terminal), enabled_ (true), prompt_ ("$"), promptWidth_ (1u), editWidth_ (0u), 
+      text_ (""), point_ (0u), displayPos_ (0u), lastKey_ (0u), callback_ (cb)
+{
+    defineKey(KeyParser::Return,    &bindings::accept);
+    defineKey(KeyParser::Right,     &bindings::forwardChar);
+    defineKey(KeyParser::Left,      &bindings::backwardChar);
+    defineKey(KeyParser::Backspace, &bindings::backwardDeleteChar);
+    defineKey(KeyParser::Delete,    &bindings::deleteChar);
+    defineKey(KeyParser::Home,      &bindings::beginningOfLine);
+    defineKey(KeyParser::End,       &bindings::endOfLine);
+    defineKey(KeyParser::Ctrl('K'), &bindings::deleteToEndOfLine);
+    defineKey(KeyParser::Ctrl('A'), &bindings::beginningOfLine);
+    defineKey(KeyParser::Ctrl('E'), &bindings::endOfLine);
+    defineKey(KeyParser::Ctrl('D'), &bindings::deleteChar);
+    defineKey(KeyParser::Ctrl('C'), &bindings::restartEdit);
+}
+
+prefix_ void senf::term::LineEditor::prompt(std::string const & text)
+{
+    prompt_ = text;
+    promptWidth_ = prompt_.size();
+    editWidth_ = width() - promptWidth_ - 3;
+    if (enabled_)
+        redisplay();
+}
+
+prefix_ void senf::term::LineEditor::set(std::string const & text, unsigned pos)
+{
+    text_ = text;
+    point_ = pos;
+    if (point_ > text.size())
+        point_ = text.size();
+    displayPos_ = 0u;
+    if (point_ > editWidth_)
+        displayPos_ = point_ - editWidth_;
+    redisplay();
+}
+
+prefix_ void senf::term::LineEditor::show()
+{
+    if (enabled_)
+        return;
+    enabled_ = true;
+    redisplay();
+}
+
+prefix_ void senf::term::LineEditor::hide()
+{
+    if (! enabled_)
+        return;
+    clearLine();
+    enabled_ = false;
+}
+
+prefix_ void senf::term::LineEditor::accept()
+{
+    if (enabled_)
+        newline();
+    hide();
+    callback_(text_);
+    clear();
+}
+
+prefix_ void senf::term::LineEditor::clear()
+{
+    set("");
+}
+
+prefix_ void senf::term::LineEditor::redisplay()
+{
+    redisplayNeeded_ = true;
+}
+
+prefix_ void senf::term::LineEditor::forceRedisplay()
+{
+    if (! enabled_)
+        return;
+    clearLine();
+    setBold();
+    put(prompt_);
+    put( displayPos_ > 0 ? '<' : ' ' );
+    if (text_.size() > displayPos_ + editWidth_) {
+        toColumn(editWidth_ + promptWidth_ + 1);
+        put('>');
+        toColumn(promptWidth_ + 1);
+    }
+    setNormal();
+    put(text_.substr(displayPos_, editWidth_));
+    toColumn(point_ - displayPos_ + promptWidth_ + 1);
+    redisplayNeeded_ = false;
+}
+
+prefix_ void senf::term::LineEditor::gotoChar(unsigned n)
+{
+    point_ = n;
+    if (point_ > text_.size())
+        point_ = text_.size();
+    if (point_ < displayPos_)
+        displayPos_ = point_;
+    if (point_ > displayPos_+editWidth_)
+        displayPos_ = point_-editWidth_;
+    redisplay();
+}
+
+prefix_ void senf::term::LineEditor::scrollTo(unsigned n)
+{
+    displayPos_ = n;
+    if (displayPos_ > text_.size())
+        displayPos_ = text_.size();
+    if (point_ < displayPos_)
+        point_ = displayPos_;
+    if (point_ > displayPos_+editWidth_)
+        point_ = displayPos_+editWidth_;
+    redisplay();
+}
+
+prefix_ void senf::term::LineEditor::deleteChar(unsigned n)
+{
+    if (point_ >= text_.size())
+        return;
+    text_.erase(point_, n);
+    redisplay();
+}
+
+prefix_ void senf::term::LineEditor::insert(char ch)
+{
+    text_.insert(point_, std::string(1, ch));
+    gotoChar(point_+1);
+    redisplay();
+}
+
+prefix_ void senf::term::LineEditor::insert(std::string const & text)
+{
+    text_.insert(point_, text);
+    gotoChar(point_+text.size());
+    redisplay();
+}
+
+prefix_ std::string const & senf::term::LineEditor::text()
+{
+    return text_;
+}
+
+prefix_ unsigned senf::term::LineEditor::point()
+{
+    return point_;
+}
+
+prefix_ unsigned senf::term::LineEditor::displayPos()
+{
+    return displayPos_;
+}
+
+prefix_ senf::term::LineEditor::keycode_t senf::term::LineEditor::lastKey()
+{
+    return lastKey_;
+}
+
+prefix_ void senf::term::LineEditor::defineKey(keycode_t key, KeyBinding binding)
+{
+    bindings_[key] = binding;
+}
+
+prefix_ void senf::term::LineEditor::unsetKey(keycode_t key)
+{
+    bindings_.erase(key);
+}
+
+prefix_ void senf::term::LineEditor::cb_init()
+{
+    BaseEditor::cb_init();
+    editWidth_ = width() - promptWidth_ - 3;
+    forceRedisplay();
+}
+
+prefix_ void senf::term::LineEditor::cb_windowSizeChanged()
+{
+    BaseEditor::cb_windowSizeChanged();
+    editWidth_ = width() - promptWidth_ - 3;
+    gotoChar(point_);
+    forceRedisplay();
+}
+
+prefix_ void senf::term::LineEditor::v_keyReceived(keycode_t key)
+{
+    lastKey_ = key;
+    KeyMap::iterator i (bindings_.find(key));
+    if (i != bindings_.end())
+        i->second(*this);
+    else if (key >= ' ' && key < 256)
+        insert(char(key));
+    if (redisplayNeeded_)
+        forceRedisplay();
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+prefix_ void senf::term::bindings::selfInsertCommand(LineEditor & editor)
+{
+    LineEditor::keycode_t key (editor.lastKey());
+    if (key >= ' ' && key < 256)
+        editor.insert(key);
+}
+
+prefix_ void senf::term::bindings::forwardChar(LineEditor & editor)
+{
+    editor.gotoChar(editor.point()+1);
+}
+
+prefix_ void senf::term::bindings::backwardChar(LineEditor & editor)
+{
+    unsigned p (editor.point());
+    if (p>0)
+        editor.gotoChar(p-1);
+}
+
+prefix_ void senf::term::bindings::accept(LineEditor & editor)
+{
+    editor.accept();
+}
+
+prefix_ void senf::term::bindings::backwardDeleteChar(LineEditor & editor)
+{
+    unsigned p (editor.point());
+    if (p>0) {
+        editor.gotoChar(p-1);
+        editor.deleteChar();
+    }
+}
+
+prefix_ void senf::term::bindings::deleteChar(LineEditor & editor)
+{
+    editor.deleteChar();
+}
+
+prefix_ void senf::term::bindings::beginningOfLine(LineEditor & editor)
+{
+    editor.gotoChar(0u);
+}
+
+prefix_ void senf::term::bindings::endOfLine(LineEditor & editor)
+{
+    editor.gotoChar(editor.text().size());
+}
+
+prefix_ void senf::term::bindings::deleteToEndOfLine(LineEditor & editor)
+{
+    editor.deleteChar(editor.text().size()-editor.point());
+}
+
+prefix_ void senf::term::bindings::restartEdit(LineEditor & editor)
+{
+    editor.newline();
+    editor.clear();
+    editor.redisplay();
+}
+
 ///////////////////////////////cc.e////////////////////////////////////////
 #undef prefix_
 //#include "Editor.mpp"
index 6253423..de94ae1 100644 (file)
@@ -27,6 +27,7 @@
 #define HH_SENF_Utils_Termlib_Editor_ 1
 
 // Custom includes
+#include <map>
 #include <senf/Scheduler/ClockService.hh>
 #include <senf/Scheduler/TimerEvent.hh>
 #include "AbstractTerminal.hh"
@@ -50,23 +51,27 @@ namespace term {
 
         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
+        void put(char ch);              ///< Write \p ch at current column
+        void put(std::string const & text);
+        void clearLine();               ///< Clear current line and move cursor to first column
+        void setBold();                 ///< Set bold char display
+        void setNormal();               ///< Set normal char display
 
         unsigned currentColumn() const; ///< Return number of current column
+        unsigned width();
+
+    protected:
+        virtual void cb_init();
+        virtual void cb_windowSizeChanged();
 
     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);
         
@@ -79,6 +84,87 @@ namespace term {
         unsigned column_;
     };
 
+    class LineEditor
+        : public BaseEditor
+    {
+    public:
+        ///////////////////////////////////////////////////////////////////////////
+        // Types
+
+        typedef boost::function<void (LineEditor&)> KeyBinding;
+        typedef boost::function<void (std::string const &)> AcceptCallback;
+
+        ///////////////////////////////////////////////////////////////////////////
+
+        LineEditor(AbstractTerminal & terminal, AcceptCallback cb);
+        
+        ///////////////////////////////////////////////////////////////////////////
+
+        void prompt(std::string const & text);
+        void set(std::string const & text, unsigned pos = 0u);
+
+        // Overall edit control
+        void show();
+        void hide();
+        void accept();
+        void clear();
+        void redisplay();
+        void forceRedisplay();
+        
+        // Cursor and display movement
+        void gotoChar(unsigned n);
+        void scrollTo(unsigned n);
+
+        // Text manipulation
+        void deleteChar(unsigned n=1);
+        void insert(char ch);
+        void insert(std::string const & text);
+
+        // Get information
+        std::string const & text();
+        unsigned point();
+        unsigned displayPos();
+        keycode_t lastKey();
+
+        // Key bindings
+        void defineKey(keycode_t key, KeyBinding binding);
+        void unsetKey(keycode_t key);
+        
+    private:
+        virtual void cb_init();
+        virtual void cb_windowSizeChanged();
+        virtual void v_keyReceived(keycode_t key);
+
+        typedef std::map<keycode_t, KeyBinding> KeyMap;
+
+        bool enabled_;
+        bool redisplayNeeded_;
+        std::string prompt_;
+        unsigned promptWidth_;
+        unsigned editWidth_;
+        std::string text_;
+        unsigned point_;
+        unsigned displayPos_;
+        keycode_t lastKey_;
+        AcceptCallback callback_;
+        KeyMap bindings_;
+    };
+
+namespace bindings {
+
+    void selfInsertCommand   (LineEditor & editor);
+    void forwardChar         (LineEditor & editor);
+    void backwardChar        (LineEditor & editor);
+    void accept              (LineEditor & editor);
+    void backwardDeleteChar  (LineEditor & editor);
+    void deleteChar          (LineEditor & editor);
+    void beginningOfLine     (LineEditor & editor);
+    void endOfLine           (LineEditor & editor);
+    void deleteToEndOfLine   (LineEditor & editor);
+    void restartEdit         (LineEditor & editor);
+
+}
+
 }}
 
 ///////////////////////////////hh.e////////////////////////////////////////
index 7ad201f..232a188 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);
 }
 
@@ -359,28 +359,25 @@ prefix_ std::string senf::term::Terminfo::formatString(properties::String p,
 
 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 "";
@@ -405,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)
@@ -420,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)
@@ -429,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;
     }
 
@@ -438,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 && *i < stringPool_.size())
             *j = &(stringPool_[0]) + *i;
+        else
+            *j = 0;
 }
 
 ///////////////////////////////////////////////////////////////////////////
index f001df8..616f374 100644 (file)
@@ -198,26 +198,28 @@ namespace term {
         // Types
 
         enum KeyCode {
-            Space = ' ', Tab = '\t', First = 0xE000, Esc = First, Backspace, Backtab, Begin, CATab,
-            CTab, Cancel, Center, Clear, ClearToEOL, ClearToEOS, Close, Command, Copy, Create,
-            Delete, DeleteLine, Down, DownLeft, DownRight, End, Enter, Exit, F0, F1, F2, F3, F4, F5,
-            F6, F7, F8, F9, F10, F11, F12, F13, F14, F15, F16, F17, F18, F19, F20, F21, F22, F23,
-            F24, F25, F26, F27, F28, F29, F30, F31, F32, F33, F34, F35, F36, F37, F38, F39, F40,
-            F41, F42, F43, F44, F45, F46, F47, F48, F49, F50, F51, F52, F53, F54, F55, F56, F57,
-            F58, F59, F60, F61, F62, F63, Find, Help, Home, Insert, InsertLine, Left, Mark, Message,
-            Mouse, Move, Next, Open, Options, PageDown, PageUp, Previous, Print, Redo, Reference,
-            Refresh, Replace, Restart, Resume, Right, Save, Select, ShiftBegin, ShiftCancel,
-            ShiftCommand, ShiftCopy, ShiftCreate, ShiftDelete, ShiftDeleteLine, ShiftEnd,
-            ShiftClearToEOL, ShiftExit, ShiftFind, ShiftHelp, ShiftHome, ShiftInsert, ShiftLeft,
-            ShiftMessage, ShiftMove, ShiftNext, ShiftOptions, ShiftPrevious, ShiftPrint, ShiftRedo,
-            ShiftReplace, ShiftResume, ShiftRight, ShiftSave, ShiftSuspend, ShiftTab, ShiftUndo,
-            Suspend, Undo, Up, UpLeft, UpRight, Incomplete = 0xE0FF };
+            Space = ' ', Tab = '\t', Return = '\r', First = 0xE000, Esc = First, Backspace, Backtab,
+            Begin, CATab, CTab, Cancel, Center, Clear, ClearToEOL, ClearToEOS, Close, Command, Copy,
+            Create, Delete, DeleteLine, Down, DownLeft, DownRight, End, Enter, Exit, F0, F1, F2, F3,
+            F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15, F16, F17, F18, F19, F20, F21, F22,
+            F23, F24, F25, F26, F27, F28, F29, F30, F31, F32, F33, F34, F35, F36, F37, F38, F39,
+            F40, F41, F42, F43, F44, F45, F46, F47, F48, F49, F50, F51, F52, F53, F54, F55, F56,
+            F57, F58, F59, F60, F61, F62, F63, Find, Help, Home, Insert, InsertLine, Left, Mark,
+            Message, Mouse, Move, Next, Open, Options, PageDown, PageUp, Previous, Print, Redo,
+            Reference, Refresh, Replace, Restart, Resume, Right, Save, Select, ShiftBegin,
+            ShiftCancel, ShiftCommand, ShiftCopy, ShiftCreate, ShiftDelete, ShiftDeleteLine,
+            ShiftEnd, ShiftClearToEOL, ShiftExit, ShiftFind, ShiftHelp, ShiftHome, ShiftInsert,
+            ShiftLeft, ShiftMessage, ShiftMove, ShiftNext, ShiftOptions, ShiftPrevious, ShiftPrint,
+            ShiftRedo, ShiftReplace, ShiftResume, ShiftRight, ShiftSave, ShiftSuspend, ShiftTab,
+            ShiftUndo, Suspend, Undo, Up, UpLeft, UpRight, Incomplete = 0xE0FF };
 
         static char const * const KeyNames[];
 
         typedef wchar_t keycode_t;
         typedef std::string::size_type size_type;
 
+        static keycode_t Ctrl(char ch) { return ch-'@'; }
+
         ///////////////////////////////////////////////////////////////////////////
 
         KeyParser();
index a02711f..4f9ccf2 100644 (file)
@@ -28,6 +28,7 @@
 
 // Custom includes
 #include <boost/bind.hpp>
+#include "../../Utils/membind.hh"
 #include "../../Scheduler/Scheduler.hh"
 #include "../Logger.hh"
 #include "../../Socket/Protocols/INet.hh"
 ///////////////////////////////cc.p////////////////////////////////////////
 
 namespace {
-
-    class MyEditor
-        : public senf::term::BaseEditor
-    {
-    public:
-        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) {}
-        
+            : senf::term::BaseTelnetProtocol (handle), 
+              editor_ (*this, senf::membind(&MyTelnet::executeLine, this)) 
+            {
+                editor_.prompt("myTelnet$");
+            }
+
         virtual void v_eof()
             {
                 SENF_LOG(("EOF"));
                 delete this;
             }
 
+        virtual void executeLine(std::string const & text)
+            {
+                SENF_LOG(("Execute line: " << text));
+                editor_.show();
+            }
+
     private:
-        MyEditor editor_;
+        senf::term::LineEditor editor_;
     };
 
     typedef senf::TCPv4ServerSocketHandle ServerHandle;
@@ -111,7 +79,15 @@ namespace {
         }
         ClientHandle client (handle.accept());
         SENF_LOG(("new client ..."));
-        new MyTelnet (client);
+        try {
+            new MyTelnet (client);
+        }
+        catch (std::exception & ex) {
+            SENF_LOG(("Client open failed: " << ex.what()));
+        }
+        catch (...) {
+            SENF_LOG(("Client open failed: unknown exception"));
+        }
     }
 
 }