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