LineEditor.cc
Go to the documentation of this file.
1 //
2 // Copyright (c) 2020 Fraunhofer Institute for Applied Information Technology (FIT)
3 // Network Research Group (NET)
4 // Schloss Birlinghoven, 53754 Sankt Augustin, GERMANY
5 // Contact: support@wiback.org
6 //
7 // This file is part of the SENF code tree.
8 // It is licensed under the 3-clause BSD License (aka New BSD License).
9 // See LICENSE.txt in the top level directory for details or visit
10 // https://opensource.org/licenses/BSD-3-Clause
11 //
12 
13 
17 #include "LineEditor.hh"
18 //#include "LineEditor.ih"
19 
20 // Custom includes
22 #include <senf/Utils/Range.hh>
23 #include <senf/Utils/membind.hh>
24 
25 //#include "LineEditor.mpp"
26 #define prefix_
27 //-/////////////////////////////////////////////////////////////////////////////////////////////////
28 
29 //-/////////////////////////////////////////////////////////////////////////////////////////////////
30 // senf::console::detail::LineEditorSwitcher
31 
33  : ClientReader(client),
34  reader_ (new LineEditorClientReader(client, *this))
35 {}
36 
38 {
39  // We need to delete the old reader *before* creating the new one such that all old scheduler
40  // events are removed before installing the new ones.
41  reader_.reset(0);
42  reader_.reset(new DumbClientReader(client()));
43 }
44 
45 prefix_ void senf::console::detail::LineEditorSwitcher::v_disablePrompt()
46 {
47  reader_->disablePrompt();
48 }
49 
50 prefix_ void senf::console::detail::LineEditorSwitcher::v_enablePrompt()
51 {
52  reader_->enablePrompt();
53 }
54 
55 prefix_ void senf::console::detail::LineEditorSwitcher::v_write(std::string const & data)
56 {
57  reader_->write(data);
58 }
59 
60 prefix_ unsigned senf::console::detail::LineEditorSwitcher::v_width()
61  const
62 {
63  return reader_->width();
64 }
65 
66 //-/////////////////////////////////////////////////////////////////////////////////////////////////
67 // senf::console::detail::LineEditorClientReader
68 
71  : term::BaseTelnetProtocol(client.handle()), ClientReader(client),
72  editor_ (*this, senf::membind(&LineEditorClientReader::executeLine, this)),
73  switcher_ (&switcher)
74 {
75  editor_.prompt(promptString());
77  senf::membind(&LineEditorClientReader::deleteCharOrExit, this));
79  boost::bind(&term::bindings::complete,
80  _1,
81  senf::membind(&LineEditorClientReader::completePath, this)));
82  editor_.defineKey(senf::term::KeyParser::Return, &senf::term::bindings::acceptWithRepeat);
83 }
84 
85 prefix_ void senf::console::detail::LineEditorClientReader::v_setupFailed()
86 {
87  // Commits suicide
88  switcher_->editorSetupFailed();
89 }
90 
91 prefix_ void senf::console::detail::LineEditorClientReader::v_eof()
92 {
93  stopClient();
94 }
95 
96 prefix_ void senf::console::detail::LineEditorClientReader::v_disablePrompt()
97 {
98  editor_.hide();
99 }
100 
101 prefix_ void senf::console::detail::LineEditorClientReader::v_enablePrompt()
102 {
103  editor_.show();
104 }
105 
106 prefix_ void senf::console::detail::LineEditorClientReader::v_write(std::string const & data)
107 {
108  BaseTelnetProtocol::write(data);
109 }
110 
111 prefix_ unsigned senf::console::detail::LineEditorClientReader::v_width()
112  const
113 {
114  return editor_.width();
115 }
116 
117 prefix_ void
118 senf::console::detail::LineEditorClientReader::executeLine(std::string const & text)
119 {
120  handleInput(text);
121  stream() << std::flush;
122  editor_.prompt(promptString());
123  editor_.show();
124 }
125 
126 prefix_ void
127 senf::console::detail::LineEditorClientReader::deleteCharOrExit(term::LineEditor & editor)
128 {
129  if (editor.text().empty())
130  ClientReader::handle().facet<TCPSocketProtocol>().shutdown(TCPSocketProtocol::ShutRD);
131  else
132  term::bindings::deleteChar(editor);
133 }
134 
135 prefix_ void senf::console::detail::LineEditorClientReader::
136 completePath(term::LineEditor & editor, unsigned & b, unsigned & e, std::string & prefix,
137  std::vector<std::string> & completions)
138 {
139  std::string const & t (editor.text());
140  // Search backward from e finding the longest valid path. This does *not* accept all valid
141  // path's, only those without embedded white-space. However, this is only for completion so
142  // it's ok.
143  if (b<e) {
144  unsigned bb (e-1);
145  for (;;) {
146  if (! CommandParser::isWordChar(t[bb]) && t[bb] != '/') {
147  ++bb;
148  break;
149  }
150  if (bb == b)
151  break;
152  --bb;
153  }
154  b = bb;
155  }
156  std::string base (t.substr(b,e));
158  ParseCommandInfo cmd;
159  try {
160  parser.parsePath(base, cmd);
161  }
163  return;
164  }
165 
167  if (path.empty()) {
168  DirectoryNode::ChildrenRange cs (client().cwd().children());
169  for (DirectoryNode::ChildrenRange::iterator i (cs.begin()); i != cs.end(); ++i)
170  completions.push_back(i->first + (i->second->followLink().isDirectory() ? "/" : " "));
171  return;
172  }
173 
174  ParseCommandInfo::TokensRange::const_iterator i (path.begin());
175  ParseCommandInfo::TokensRange::const_iterator const i_end (boost::prior(path.end()));
176  DirectoryNode * dir (& client().cwd());
177  for (; i != i_end; ++i)
178  if (*i == NoneToken()) {
179  if (i == path.begin()) {
180  dir = & client().root();
181  prefix = "/";
182  }
183  }
184  else if (*i == WordToken("..")) {
185  DirectoryNode * parent (dir->parent().get());
186  if (parent) dir = parent;
187  prefix += "../";
188  }
189  else if (*i == WordToken("."))
190  prefix += "./";
191  else {
192  if (dir->hasChild(i->value())) {
193  try {
194  dir = & dir->getDirectory(i->value());
195  }
196  catch (std::bad_cast &) {
197  return;
198  }
199  prefix += i->value() + "/";
200  }
201  else {
202  DirectoryNode::ChildrenRange cs (dir->completions(i->value()));
203  if (has_one_elt(cs)) {
204  GenericNode & node (cs.begin()->second->followLink());
205  if (!node.isDirectory())
206  return;
207  dir = static_cast<DirectoryNode*>(&node);
208  prefix += cs.begin()->first + "/";
209  }
210  else
211  return;
212  }
213  }
214 
215  DirectoryNode::ChildrenRange cs (dir->completions(i->value()));
216  for (DirectoryNode::ChildrenRange::iterator j (cs.begin()); j != cs.end(); ++j)
217  completions.push_back(j->first + (j->second->followLink().isDirectory() ? "/" : " "));
218 }
219 
220 //-/////////////////////////////////////////////////////////////////////////////////////////////////
221 #undef prefix_
222 //#include "LineEditor.mpp"
223 
224 
225 // Local Variables:
226 // mode: c++
227 // fill-column: 100
228 // comment-column: 40
229 // c-file-style: "senf"
230 // indent-tabs-mode: nil
231 // ispell-local-dictionary: "american"
232 // compile-command: "scons -u test"
233 // End:
LineEditorClientReader(Client &client, LineEditorSwitcher &switcher)
Definition: LineEditor.cc:70
Exception thrown when the parser detects an error.
Definition: Parse.hh:656
ChildrenRange completions(std::string const &s) const
Return iterator range of completions for s.
void defineKey(keycode_t key, KeyBinding binding)
Parse commands.
Definition: Parse.hh:602
unspecified_keyword_type parser
Argument parser.
Config/console tree directory node.
Definition: Node.hh:406
u8 data[SPECTRAL_HT20_NUM_BINS]
boost::function< R(Args)> membind(R(T::*fn)(Args), T *ob)
boost::iterator_range< ChildMap::const_iterator > ChildrenRange
Definition: Node.hh:418
DirectoryNode & getDirectory(std::string const &name) const
Get directory child node (dereferencing links)
static bool isWordChar(char ch)
Check, if ch is a word character.
Definition: Parse.cc:482
bool hasChild(std::string const &name) const
true, if there is a child with name name
Definition: Config.hh:28
void prompt(std::string const &text)
Single parsed console command.
Definition: Parse.hh:363
TokensRange commandPath() const
Command path.
bool has_one_elt(Range r)
Server client instance.
Definition: Server.hh:143
std::string const & text()
static keycode_t Ctrl(char ch)
Internal: ClientReader using senf::term::LineEditor for interactive input.
Definition: LineEditor.hh:63
LineEditor public header.
boost::shared_ptr< DirectoryNode > parent() const
Parent node.
boost::iterator_range< token_iterator > TokensRange
Definition: Parse.hh:378
friend DirectoryNode & root()
Get console root node.
Config/console node tree base-class.
Definition: Node.hh:250
Internal: Client reader switching between LineEditorClientReader or DumbClientReader.
Definition: LineEditor.hh:42
#define prefix_
Definition: LineEditor.cc:26
unsigned width() const