WvStreams
|
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, WvReadLineStream &local) 00154 { 00155 const char *line = remote.getline(); 00156 if (line == NULL) 00157 return; 00158 00159 WvStringList words; 00160 wvtcl_decode(words, line); 00161 00162 WvString first = words.popstr(); 00163 bool last_line = !!first && first != "-"; 00164 if (last_line) 00165 local.print("%s ", first); 00166 local.print("%s\n", words.join(" ")); 00167 if (last_line) 00168 local.display_prompt(); 00169 00170 if (words.popstr() == "Commands availible:") 00171 local.set_commands(words); 00172 } 00173 00174 00175 void local_cb(WvReadLineStream &local, WvStream &remote) 00176 { 00177 const char *line = local.getline(); 00178 if (line == NULL) 00179 return; 00180 00181 if (strcmp(line, "quit") == 0) 00182 remote.close(); 00183 00184 remote.print("%s\n", line); 00185 } 00186 00187 00188 int main(int argc, char **argv) 00189 { 00190 WvReadLineStream readlinestream(wvcon, "> "); 00191 00192 const char *sockname = "/tmp/weaver.wsd"; 00193 if (argc >= 2) 00194 sockname = argv[1]; 00195 00196 WvUnixConn *s = new WvUnixConn(sockname); 00197 if (!s->isok()) 00198 { 00199 wverr->print("Failed to connect to %s: %s\n", 00200 sockname, s->errstr()); 00201 return 1; 00202 } 00203 s->set_wsname("%s", sockname); 00204 s->print("help\n"); 00205 00206 s->setcallback(wv::bind(remote_cb, wv::ref(*s), wv::ref(readlinestream))); 00207 WvIStreamList::globallist.append(s, true, "wvstreams debugger client"); 00208 00209 readlinestream.setcallback(wv::bind(local_cb, wv::ref(readlinestream), 00210 wv::ref(*s))); 00211 WvIStreamList::globallist.append(&readlinestream, false, 00212 "wvstreams debugger readline"); 00213 00214 while (s->isok() && readlinestream.isok()) 00215 WvIStreamList::globallist.runonce(); 00216 00217 return 0; 00218 } 00219