00001 #include "wvfdstream.h"
00002 #include "wvistreamlist.h"
00003 #include "wvstrutils.h"
00004 #include "wvunixsocket.h"
00005 #include <readline/readline.h>
00006 #include <readline/history.h>
00007
00008 #ifndef MACOS // The version of READLINE shipped with MacOS is brain damaged.
00009
00010 class WvReadLineStream : public WvStream
00011 {
00012 static WvReadLineStream *me;
00013 WvStream *base;
00014 WvString prompt;
00015 WvDynBuf line_buf;
00016 WvStringList commands;
00017
00018 virtual size_t uread(void *_buf, size_t count)
00019 {
00020 size_t result = 0;
00021 char *buf = (char *)_buf;
00022 while (count > 0 && line_buf.used() > 0)
00023 {
00024 size_t chunk = line_buf.optgettable();
00025 if (chunk > count)
00026 chunk = count;
00027 memcpy(buf, line_buf.get(chunk), chunk);
00028 count -= chunk;
00029 buf += chunk;
00030 result += chunk;
00031 }
00032 return result;
00033 }
00034
00035 virtual size_t uwrite(const void *_buf, size_t count)
00036 {
00037 const char *buf = (const char *)_buf;
00038 for (size_t i=0; i<count; ++i)
00039 {
00040 if (buf[i] == '\n')
00041 rl_crlf();
00042 else
00043 rl_show_char(buf[i]);
00044 }
00045 return count;
00046 }
00047
00048 static void readline_callback(char *str)
00049 {
00050 if (str == NULL)
00051 return;
00052 size_t len = strlen(str);
00053 if (len == 0)
00054 return;
00055 me->line_buf.put(str, len);
00056 me->line_buf.putch('\n');
00057 add_history(str);
00058 }
00059
00060 static int readline_getc(FILE *)
00061 {
00062 char ch;
00063 assert(me->base->read(&ch, 1) == 1);
00064 return ch;
00065 }
00066
00067 static char *readline_command_completion_function(const char *text, int state)
00068 {
00069 static int skip = 0;
00070 if (state == 0)
00071 skip = 0;
00072 int my_skip = skip;
00073 size_t len = strlen(text);
00074 WvStringList::Iter i(me->commands);
00075 for (i.rewind(); i.next(); )
00076 {
00077 if (my_skip-- > 0)
00078 continue;
00079 ++skip;
00080 if (i->len() >= len && strncmp(*i, text, len) == 0)
00081 return strdup(*i);
00082 }
00083 return NULL;
00084 }
00085
00086 virtual void pre_select(SelectInfo &si)
00087 {
00088 if (si.wants.readable && line_buf.used() > 0)
00089 si.msec_timeout = 0;
00090
00091 base->pre_select(si);
00092 }
00093
00094 virtual bool post_select(SelectInfo &si)
00095 {
00096 bool now = false;
00097 if (si.wants.readable && line_buf.used() > 0)
00098 now = true;
00099
00100 while (base->isreadable())
00101 rl_callback_read_char();
00102 return base->post_select(si) || now;
00103 }
00104
00105 public:
00106
00107 WvReadLineStream(WvStream *_base, WvStringParm _prompt)
00108 {
00109 base = _base;
00110 prompt = _prompt;
00111
00112 assert(!me);
00113 me = this;
00114 set_wsname("readline on %s", base->wsname());
00115 rl_already_prompted = 1;
00116 rl_completion_entry_function = readline_command_completion_function;
00117 rl_callback_handler_install(prompt, readline_callback);
00118 rl_getc_function = readline_getc;
00119 }
00120
00121 ~WvReadLineStream()
00122 {
00123 rl_getc_function = NULL;
00124 rl_callback_handler_remove();
00125 me = NULL;
00126 }
00127
00128 virtual bool isok() const
00129 {
00130 return WvStream::isok() && base->isok();
00131 }
00132
00133 void display_prompt()
00134 {
00135 base->print("%s", prompt);
00136 rl_already_prompted = 1;
00137 }
00138
00139 void set_commands(const WvStringList &_commands)
00140 {
00141 commands.zap();
00142 WvStringList::Iter i(_commands);
00143 for (i.rewind(); i.next(); )
00144 commands.append(*i);
00145 }
00146
00147 const char *wstype() const { return "WvReadLineStream"; }
00148 };
00149
00150
00151 WvReadLineStream *WvReadLineStream::me = NULL;
00152
00153
00154 void remote_cb(WvStream &remote, WvReadLineStream &local)
00155 {
00156 const char *line = remote.getline();
00157 if (line == NULL)
00158 return;
00159
00160 WvStringList words;
00161 wvtcl_decode(words, line);
00162
00163 WvString first = words.popstr();
00164 bool last_line = !!first && first != "-";
00165 if (last_line)
00166 local.print("%s ", first);
00167 local.print("%s\n", words.join(" "));
00168 if (last_line)
00169 local.display_prompt();
00170
00171 if (words.popstr() == "Commands availible:")
00172 local.set_commands(words);
00173 }
00174
00175
00176 void local_cb(WvReadLineStream &local, WvStream &remote)
00177 {
00178 const char *line = local.getline();
00179 if (line == NULL)
00180 return;
00181
00182 if (strcmp(line, "quit") == 0)
00183 remote.close();
00184
00185 remote.print("%s\n", line);
00186 }
00187
00188
00189 int main(int argc, char **argv)
00190 {
00191 WvReadLineStream readlinestream(wvcon, "> ");
00192
00193 const char *sockname = "/tmp/weaver.wsd";
00194 if (argc >= 2)
00195 sockname = argv[1];
00196
00197 WvUnixConn *s = new WvUnixConn(sockname);
00198 if (!s->isok())
00199 {
00200 wverr->print("Failed to connect to %s: %s\n",
00201 sockname, s->errstr());
00202 return 1;
00203 }
00204 s->set_wsname("%s", sockname);
00205 s->print("help\n");
00206
00207 s->setcallback(wv::bind(remote_cb, wv::ref(*s), wv::ref(readlinestream)));
00208 WvIStreamList::globallist.append(s, true, "wvstreams debugger client");
00209
00210 readlinestream.setcallback(wv::bind(local_cb, wv::ref(readlinestream),
00211 wv::ref(*s)));
00212 WvIStreamList::globallist.append(&readlinestream, false,
00213 "wvstreams debugger readline");
00214
00215 while (s->isok() && readlinestream.isok())
00216 WvIStreamList::globallist.runonce();
00217
00218 return 0;
00219 }
00220
00221 #endif // Apple brain damaged Readline.