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.