WvStreams
wsd.cc
1 #include "wvfdstream.h"
2 #include "wvistreamlist.h"
3 #include "wvstrutils.h"
4 #include "wvunixsocket.h"
5 #include <readline/readline.h>
6 #include <readline/history.h>
7 
8 #ifndef MACOS // The version of READLINE shipped with MacOS is brain damaged.
9 
10 class WvReadLineStream : public WvStream
11 {
12  static WvReadLineStream *me;
13  WvStream *base;
14  WvString prompt;
15  WvDynBuf line_buf;
16  WvStringList commands;
17 
18  virtual size_t uread(void *_buf, size_t count)
19  {
20  size_t result = 0;
21  char *buf = (char *)_buf;
22  while (count > 0 && line_buf.used() > 0)
23  {
24  size_t chunk = line_buf.optgettable();
25  if (chunk > count)
26  chunk = count;
27  memcpy(buf, line_buf.get(chunk), chunk);
28  count -= chunk;
29  buf += chunk;
30  result += chunk;
31  }
32  return result;
33  }
34 
35  virtual size_t uwrite(const void *_buf, size_t count)
36  {
37  const char *buf = (const char *)_buf;
38  for (size_t i=0; i<count; ++i)
39  {
40  if (buf[i] == '\n')
41  rl_crlf();
42  else
43  rl_show_char(buf[i]);
44  }
45  return count;
46  }
47 
48  static void readline_callback(char *str)
49  {
50  if (str == NULL)
51  return;
52  size_t len = strlen(str);
53  if (len == 0)
54  return;
55  me->line_buf.put(str, len);
56  me->line_buf.putch('\n');
57  add_history(str);
58  }
59 
60  static int readline_getc(FILE *)
61  {
62  char ch;
63  assert(me->base->read(&ch, 1) == 1);
64  return ch;
65  }
66 
67  static char *readline_command_completion_function(const char *text, int state)
68  {
69  static int skip = 0;
70  if (state == 0)
71  skip = 0;
72  int my_skip = skip;
73  size_t len = strlen(text);
74  WvStringList::Iter i(me->commands);
75  for (i.rewind(); i.next(); )
76  {
77  if (my_skip-- > 0)
78  continue;
79  ++skip;
80  if (i->len() >= len && strncmp(*i, text, len) == 0)
81  return strdup(*i);
82  }
83  return NULL;
84  }
85 
86  virtual void pre_select(SelectInfo &si)
87  {
88  if (si.wants.readable && line_buf.used() > 0)
89  si.msec_timeout = 0;
90 
91  base->pre_select(si);
92  }
93 
94  virtual bool post_select(SelectInfo &si)
95  {
96  bool now = false;
97  if (si.wants.readable && line_buf.used() > 0)
98  now = true;
99 
100  while (base->isreadable())
101  rl_callback_read_char();
102  return base->post_select(si) || now;
103  }
104 
105 public:
106 
107  WvReadLineStream(WvStream *_base, WvStringParm _prompt)
108  {
109  base = _base;
110  prompt = _prompt;
111 
112  assert(!me);
113  me = this;
114  set_wsname("readline on %s", base->wsname());
115  rl_already_prompted = 1;
116  rl_completion_entry_function = readline_command_completion_function;
117  rl_callback_handler_install(prompt, readline_callback);
118  rl_getc_function = readline_getc;
119  }
120 
122  {
123  rl_getc_function = NULL;
124  rl_callback_handler_remove();
125  me = NULL;
126  }
127 
128  virtual bool isok() const
129  {
130  return WvStream::isok() && base->isok();
131  }
132 
133  void display_prompt()
134  {
135  base->print("%s", prompt);
136  rl_already_prompted = 1;
137  }
138 
139  void set_commands(const WvStringList &_commands)
140  {
141  commands.zap();
142  WvStringList::Iter i(_commands);
143  for (i.rewind(); i.next(); )
144  commands.append(*i);
145  }
146 
147  const char *wstype() const { return "WvReadLineStream"; }
148 };
149 
150 
151 WvReadLineStream *WvReadLineStream::me = NULL;
152 
153 
154 void remote_cb(WvStream &remote, WvReadLineStream &local)
155 {
156  const char *line = remote.getline();
157  if (line == NULL)
158  return;
159 
160  WvStringList words;
161  wvtcl_decode(words, line);
162 
163  WvString first = words.popstr();
164  bool last_line = !!first && first != "-";
165  if (last_line)
166  local.print("%s ", first);
167  local.print("%s\n", words.join(" "));
168  if (last_line)
169  local.display_prompt();
170 
171  if (words.popstr() == "Commands availible:")
172  local.set_commands(words);
173 }
174 
175 
176 void local_cb(WvReadLineStream &local, WvStream &remote)
177 {
178  const char *line = local.getline();
179  if (line == NULL)
180  return;
181 
182  if (strcmp(line, "quit") == 0)
183  remote.close();
184 
185  remote.print("%s\n", line);
186 }
187 
188 
189 int main(int argc, char **argv)
190 {
191  WvReadLineStream readlinestream(wvcon, "> ");
192 
193  const char *sockname = "/tmp/weaver.wsd";
194  if (argc >= 2)
195  sockname = argv[1];
196 
197  WvUnixConn *s = new WvUnixConn(sockname);
198  if (!s->isok())
199  {
200  wverr->print("Failed to connect to %s: %s\n",
201  sockname, s->errstr());
202  return 1;
203  }
204  s->set_wsname("%s", sockname);
205  s->print("help\n");
206 
207  s->setcallback(wv::bind(remote_cb, wv::ref(*s), wv::ref(readlinestream)));
208  WvIStreamList::globallist.append(s, true, "wvstreams debugger client");
209 
210  readlinestream.setcallback(wv::bind(local_cb, wv::ref(readlinestream),
211  wv::ref(*s)));
212  WvIStreamList::globallist.append(&readlinestream, false,
213  "wvstreams debugger readline");
214 
215  while (s->isok() && readlinestream.isok())
216  WvIStreamList::globallist.runonce();
217 
218  return 0;
219 }
220 
221 #endif // Apple brain damaged Readline.
WvBufBaseCommonImpl::get
const T * get(size_t count)
Reads exactly the specified number of elements and returns a pointer to a storage location owned by t...
Definition: wvbufbase.h:114
WvStream::runonce
void runonce(time_t msec_timeout=-1)
Exactly the same as: if (select(timeout)) callback();.
Definition: wvstream.h:391
WvFdStream::isok
virtual bool isok() const
return true if the stream is actually usable right now
Definition: wvfdstream.cc:134
WvStringList::popstr
WvString popstr()
get the first string in the list, or an empty string if the list is empty.
Definition: wvstringlist.cc:55
WvBufBaseCommonImpl::optgettable
size_t optgettable() const
Returns the optimal maximum number of elements in the buffer currently available for reading without ...
Definition: wvbufbase.h:154
WvBufBase< unsigned char >::putch
void putch(int ch)
Puts a single character into the buffer as an int.
Definition: wvbuf.h:76
WvStream::close
virtual void close()
Close the stream if it is open; isok() becomes false from now on.
Definition: wvstream.cc:341
WvString
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:329
WvStream::isreadable
virtual bool isreadable()
Returns true if the stream is readable.
Definition: wvstream.cc:590
wvstrutils.h
IWvStream::SelectInfo
the data structure used by pre_select()/post_select() and internally by select().
Definition: iwvstream.h:50
WvStream::getline
char * getline(time_t wait_msec=0, char separator='\n', int readahead=1024)
Read up to one line of data from the stream and return a pointer to the internal buffer containing th...
Definition: wvstream.h:175
WvReadLineStream
Definition: wsd.cc:10
WvStringList::join
WvString join(const char *joinchars=" ") const
concatenates all elements of the list seperating on joinchars
Definition: wvstringlist.cc:14
WvStream::post_select
virtual bool post_select(SelectInfo &si)
post_select() is called after ::select(), and returns true if this object is now ready.
Definition: wvstream.cc:875
WvStream::read
virtual size_t read(void *buf, size_t count)
read a data block on the stream.
Definition: wvstream.cc:490
WvReadLineStream::isok
virtual bool isok() const
return true if the stream is actually usable right now
Definition: wsd.cc:128
WvStream::isok
virtual bool isok() const
return true if the stream is actually usable right now
Definition: wvstream.cc:445
WvStream
Unified support for streams, that is, sequences of bytes that may or may not be ready for read/write ...
Definition: wvstream.h:24
WvDynBufBase< unsigned char >
WvStream::setcallback
void setcallback(IWvStreamCallback _callfunc)
define the callback function for this stream, called whenever the callback() member is run,...
Definition: wvstream.cc:1130
WvBufBaseCommonImpl::used
size_t used() const
Returns the number of elements in the buffer currently available for reading.
Definition: wvbufbase.h:92
WvUnixConn
WvStream-based Unix domain socket connection class.
Definition: wvunixsocket.h:33
WvStringList
This is a WvList of WvStrings, and is a really handy way to parse strings.
Definition: wvstringlist.h:27
wvtcl_decode
void wvtcl_decode(WvList< WvString > &l, WvStringParm _s, const WvStringMask &splitchars=WVTCL_SPLITCHARS, bool do_unescape=true)
split a tcl-style list.
Definition: wvtclstring.cc:386
WvStream::pre_select
virtual void pre_select(SelectInfo &si)
pre_select() sets up for eventually calling ::select().
Definition: wvstream.cc:844