4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 // Stefan Bund <g0dil@berlios.de>
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 \brief Readline non-inline non-template implementation */
26 #include "Readline.hh"
27 //#include "Readline.ih"
31 #include <readline/readline.h>
32 #include <readline/history.h>
33 #include <boost/algorithm/string/trim.hpp>
34 #include <boost/algorithm/string/replace.hpp>
35 #include "../Utils/membind.hh"
37 //#include "Readline.mpp"
39 ///////////////////////////////cc.p////////////////////////////////////////
42 // Readline integration is a bit awkward. There are several things to it:
44 // - Readline uses global variables for all state. Therefore, we can use readline only for one
46 // - We need to make readline to read from the socket handle instead of some input stream. We can
47 // do this by setting a custom rl_getc_function.
48 // - We need to make readline to write to the NonblockingSocketOStream. This is possible in glibc
49 // by using a 'cookie stream'.
50 // - We need to correctly handle the terminal mode settings. Currently we unconditionally
51 // initialize the remote telnet by sending a fixed telnet option string and ignoring any otpions
53 // - We need to make sure, readline never uses stderr -> we must disable beeping
54 // - There are places, where readline calls read_key unconditionally even when NOT prompted by the
55 // callback that another key is available. One such place is completion. (The 'show all
56 // completions (y/n)?' question). For now, we disable completion support.
59 ///////////////////////////////////////////////////////////////////////////
60 // senf::console::detail::ReadlineClientReader
63 extern int readline_echoing_p;
64 extern int _rl_bell_preference;
66 void _rl_erase_entire_line();
72 int readline_getc_function(FILE *)
74 if (senf::console::detail::ReadlineClientReader::active())
75 return senf::console::detail::ReadlineClientReader::instance().getc();
80 void readline_callback(char * input)
82 if (senf::console::detail::ReadlineClientReader::active()) {
84 return senf::console::detail::ReadlineClientReader::instance().callback(
86 else // input == 0 -> EOF (or Ctrl-D)
87 senf::console::detail::ReadlineClientReader::instance().eof();
91 ssize_t readline_cookie_write_function(void * cookie, char const * buffer, size_t size)
93 if (senf::console::detail::ReadlineClientReader::active() && buffer)
94 senf::console::detail::ReadlineClientReader::instance().write(
95 std::string(buffer, size));
99 void readline_prep_term(int meta)
101 readline_echoing_p = 1;
104 void readline_deprep_term()
107 int restart_line(int count, int key)
109 rl_kill_full_line(count, key);
111 rl_forced_update_display();
117 prefix_ senf::console::detail::ReadlineClientReader::ReadlineClientReader(Client & client)
118 : ClientReader(client), ch_ (-1), skipChars_ (0),
119 schedBinding_ ( client.handle(),
120 senf::membind(&ReadlineClientReader::charEvent, this),
126 throw DuplicateReaderException();
129 cookie_io_functions_t cookie_io = { 0, &readline_cookie_write_function, 0, 0 };
130 rl_outstream = fopencookie(0, "a", cookie_io);
131 if (rl_outstream == 0)
132 SENF_THROW_SYSTEM_EXCEPTION("");
133 if (setvbuf(rl_outstream, 0, _IONBF, BUFSIZ) < 0)
134 SENF_THROW_SYSTEM_EXCEPTION("");
135 rl_instream = rl_outstream;
136 rl_terminal_name = "vt100";
137 strncpy(nameBuffer_, client.name().c_str(), 128);
138 nameBuffer_[127] = 0;
139 rl_readline_name = nameBuffer_;
140 rl_prep_term_function = &readline_prep_term;
141 rl_deprep_term_function = &readline_deprep_term;
142 rl_getc_function = &readline_getc_function;
143 rl_bind_key('\t', &rl_insert);
144 rl_bind_key('\x03', &restart_line);
147 // Don't ask me, where I found this ...
148 static char options[] = { 0xFF, 0xFB, 0x01, // IAC WILL ECHO
149 0xFF, 0xFE, 0x22, // IAC DONT LINEMODE
150 0xFF, 0xFB, 0x03, // IAC WILL SGA
152 handle().write(options, options+sizeof(options));
154 strncpy(promptBuffer_, promptString().c_str(), 1024);
155 promptBuffer_[1023] = 0;
156 rl_callback_handler_install(promptBuffer_, &readline_callback);
158 _rl_bell_preference = 0; // Set this *after* the config file has been read
160 schedBinding_.enable();
163 prefix_ senf::console::detail::ReadlineClientReader::~ReadlineClientReader()
165 rl_callback_handler_remove();
166 fclose(rl_outstream);
172 prefix_ void senf::console::detail::ReadlineClientReader::callback(std::string line)
176 add_history(line.c_str());
178 stream() << std::flush;
179 strncpy(promptBuffer_, promptString().c_str(), 1024);
180 promptBuffer_[1023] = 0;
181 rl_set_prompt(promptBuffer_);
184 prefix_ void senf::console::detail::ReadlineClientReader::v_disablePrompt()
186 _rl_erase_entire_line();
189 prefix_ void senf::console::detail::ReadlineClientReader::v_enablePrompt()
191 rl_forced_update_display();
194 prefix_ void senf::console::detail::ReadlineClientReader::v_translate(std::string & data)
196 boost::replace_all(data, "\n", "\n\r");
197 boost::replace_all(data, "\xff", "\xff\xff");
200 prefix_ void senf::console::detail::ReadlineClientReader::charEvent(Scheduler::EventId event)
203 if (event != Scheduler::EV_READ || handle().read(&ch, &ch+1) <= &ch) {
207 ch_ = static_cast<unsigned char>(ch);
211 else if (ch_ == 0xff)
214 rl_callback_read_char();
220 senf::console::detail::ReadlineClientReader *
221 senf::console::detail::ReadlineClientReader::instance_ (0);
223 ///////////////////////////////////////////////////////////////////////////
224 // senf::console::detail::SafeReadlineClientReader
226 prefix_ void senf::console::detail::SafeReadlineClientReader::v_disablePrompt()
228 reader_->disablePrompt();
231 prefix_ void senf::console::detail::SafeReadlineClientReader::v_enablePrompt()
233 reader_->enablePrompt();
236 prefix_ void senf::console::detail::SafeReadlineClientReader::v_translate(std::string & data)
238 reader_->translate(data);
241 ///////////////////////////////cc.e////////////////////////////////////////
243 //#include "Readline.mpp"
249 // comment-column: 40
250 // c-file-style: "senf"
251 // indent-tabs-mode: nil
252 // ispell-local-dictionary: "american"
253 // compile-command: "scons -u test"