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_;
}
}
-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_;
}
{
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));
}
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"
#define HH_SENF_Utils_Termlib_Editor_ 1
// Custom includes
+#include <map>
#include <senf/Scheduler/ClockService.hh>
#include <senf/Scheduler/TimerEvent.hh>
#include "AbstractTerminal.hh"
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);
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////////////////////////////////////////
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);
}
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 && *i < stringPool_.size())
*j = &(stringPool_[0]) + *i;
+ else
+ *j = 0;
}
///////////////////////////////////////////////////////////////////////////
// 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();
// 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;
}
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"));
+ }
}
}