fgms 0.11.8
The
FlightGear MultiPlayer Server
project
cli_client.cxx
Go to the documentation of this file.
1 //
2 // This program is free software; you can redistribute it and/or
3 // modify it under the terms of the GNU General Public License as
4 // published by the Free Software Foundation; either version 2 of the
5 // License, or (at your option) any later version.
6 //
7 // This program is distributed in the hope that it will be useful, but
8 // WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 // General Public License for more details.
11 //
12 // You should have received a copy of the GNU General Public License
13 // along with this program; if not, write to the Free Software
14 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U$
15 //
16 // Copyright (C) 2011 Oliver Schroeder
17 //
18 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include <iostream>
24 #include "common.hxx"
25 #include "cli_client.hxx"
26 
27 #ifdef __CYGWIN__
28 #include <sys/select.h>
29 #include <_timeval.h>
30 #endif
31 
32 
33 #ifdef _MSC_VER
34  #include <conio.h> // for _kbhit(), _getch
35  #define kbhit _kbhit
36  #define getchar _getch
37 
38  bool wait_for_key ( unsigned timeout_ms = 0 )
39  {
40  return WaitForSingleObject(
41  GetStdHandle( STD_INPUT_HANDLE ),
42  timeout_ms
43  ) == WAIT_OBJECT_0;
44  }
45 #endif
46 
47 namespace LIBCLI
48 {
49 
50 //////////////////////////////////////////////////////////////////////
51 //
52 //////////////////////////////////////////////////////////////////////
54 (
55  int fd
56 )
57 {
58  lines_out = 0;
59  filters = 0;
60  max_screen_lines = 22;
61  m_print_mode = PRINT_FILTERED;
62  if (fd == fileno ( stdin ))
63  { // setup terminal attributes
64  m_socket = 0;
65  #ifndef _MSC_VER
66  struct termios NewModes;
67  setbuf ( stdin, ( char* ) 0 );
68  (void) tcgetattr (fileno (stdin), &OldModes);
69  NewModes = OldModes;
70  NewModes.c_lflag &= ~ ( ICANON );
71  NewModes.c_lflag &= ~ ( ECHO | ECHOE | ECHOK );
72  NewModes.c_lflag |= ECHONL;
73  NewModes.c_cc[VMIN] = 0;
74  NewModes.c_cc[VTIME] = 1;
75  ( void ) tcsetattr ( fileno ( stdin ), TCSANOW, &NewModes );
76  #else
77  AllocConsole(); // not required, but does not seem to harm
78  freopen ( "conin$", "r", stdin ); // needed to use WaitForSingleObject(GetStdHandle( STD_INPUT_HANDLE ),timeout_ms);
79  freopen ( "conout$", "w", stdout ); // only required IFF console output redirected
80  freopen ( "conout$", "w", stderr ); // this break the redirection, so CLI can be always seen
81  #endif
82  }
83  else
84  { // setup telnet session
85  m_socket = new netSocket();
86  m_socket->setHandle (fd);
87  const char* negotiate =
88  "\xFF\xFB\x03" // WILL SUPPRESS GO AHEAD OPTION
89  "\xFF\xFB\x01" // WILL ECHO
90  "\xFF\xFD\x03" // DO SUPPRESS GO AHEAD OPTION
91  "\xFF\xFD\x01"; // DO ECHO
92  m_socket->send (negotiate, strlen ( negotiate ), 0 );
93  }
94 } // Client::Client ()
95 //////////////////////////////////////////////////////////////////////
96 
97 //////////////////////////////////////////////////////////////////////
98 //
99 //////////////////////////////////////////////////////////////////////
101 ()
102 {
103  if (m_socket == 0)
104  { // restore terminal attributes
105  #ifndef _MSC_VER
106  ( void ) tcsetattr ( fileno ( stdin ), TCSANOW, &OldModes );
107  #endif
108  }
109  else
110  {
111  m_socket->close();
112  delete m_socket;
113  }
114 } // Client::~Client ()
115 //////////////////////////////////////////////////////////////////////
116 
117 //////////////////////////////////////////////////////////////////////
118 //
119 // return 0: timeout
120 // return >0: input available
121 // return <0: error
122 //////////////////////////////////////////////////////////////////////
123 int
125 (
126  int seconds
127 )
128 {
129 #ifdef _MSC_VER
130  if (m_socket == 0)
131  {
132  return wait_for_key (seconds * 1000);
133  }
134 #endif
135  if (m_socket != 0)
136  {
137  netSocket* ListenSockets[2];
138  ListenSockets[0] = m_socket;
139  ListenSockets[1] = 0;
140  return m_socket->select ( ListenSockets, 0, seconds );
141  }
142  else
143  {
144  struct timeval tv ;
145  tv.tv_sec = seconds;
146  tv.tv_usec = 0;
147  fd_set r;
148  FD_ZERO (&r);
149  FD_SET (0, &r);
150  return ::select (FD_SETSIZE, &r, 0, 0, &tv);
151  }
152 }
153 //////////////////////////////////////////////////////////////////////
154 
155 //////////////////////////////////////////////////////////////////////
156 //
157 //////////////////////////////////////////////////////////////////////
158 int
160 (
161  unsigned char& c
162 )
163 {
164  if (m_socket != 0)
165  {
166  return m_socket->read_char(c);
167  }
168  c = getchar();
169  return 1;
170 } // :read_char ()
171 //////////////////////////////////////////////////////////////////////
172 
173 //////////////////////////////////////////////////////////////////////
174 //
175 //////////////////////////////////////////////////////////////////////
176 void
178 (
179  const char& c
180 )
181 {
182  if (m_socket != 0)
183  {
184  m_socket->send (&c, 1);
185  }
186  else
187  {
188  cout << c;
189  cout.flush();
190  }
191  // m_output.str("");
192 } // operator << ( c );
193 //////////////////////////////////////////////////////////////////////
194 
195 //////////////////////////////////////////////////////////////////////
196 //
197 //////////////////////////////////////////////////////////////////////
198 Client&
199 Client::operator <<
200 (
201  Client& (*f) (Client&)
202 )
203 {
204  return ((*f)(*this));
205 } // operator << ( Client& (*f) )
206 //////////////////////////////////////////////////////////////////////
207 
208 char*
210 (
211  int argc,
212  char** argv
213 )
214 {
215  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
216  char* p;
217  int len = 0;
218  int i;
219  for ( i = 0; i < argc; i++ )
220  {
221  if ( i )
222  {
223  len += 1;
224  }
225  len += strlen ( argv[i] );
226  }
227  p = ( char* ) malloc ( len + 1 );
228  p[0] = 0;
229  for ( i = 0; i < argc; i++ )
230  {
231  if ( i )
232  {
233  strcat ( p, " " );
234  }
235  strcat ( p, argv[i] );
236  }
237  return p;
238 }
239 
240 int
242 (
243  int argc,
244  char** argv,
245  filter_t* filt
246 )
247 {
248  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
249  match_filter_state* state;
250  if ( argc < 2 )
251  {
252  *this << UNFILTERED << "Match filter requires an argument" << CRLF;
253  return LIBCLI::ERROR_ANY;
254  }
255  filt->filter = &Client::match_filter;
256  state = new match_filter_state;
257  filt->data = state;
258  state->flags = MATCH_NORM;
259  if ( argv[0][0] == 'e' )
260  {
261  state->flags = MATCH_INVERT;
262  }
263  state->str = join_words ( argc-1, argv+1 );
264  return LIBCLI::OK;
265 }
266 
267 int
269 (
270  int argc,
271  char** argv,
272  filter_t* filt
273 )
274 {
275  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
276  range_filter_state* state;
277  char* from = 0;
278  char* to = 0;
279  if ( !strncmp ( argv[0], "bet", 3 ) ) // between
280  {
281  if ( argc < 3 )
282  {
283  *this << UNFILTERED << "Between filter requires 2 arguments" << CRLF;
284  return LIBCLI::ERROR_ANY;
285  }
286  if ( ! ( from = strdup ( argv[1] ) ) )
287  {
288  return LIBCLI::ERROR_ANY;
289  }
290  to = join_words ( argc-2, argv+2 );
291  }
292  else // begin
293  {
294  if ( argc < 2 )
295  {
296  *this << UNFILTERED << "Begin filter requires an argument" << CRLF;
297  return LIBCLI::ERROR_ANY;
298  }
299  from = join_words ( argc-1, argv+1 );
300  }
301  filt->filter = &Client::range_filter;
302  state = new range_filter_state;
303  filt->data = state;
304  state->matched = 0;
305  state->from = from;
306  state->to = to;
307  return LIBCLI::OK;
308 }
309 
310 int
312 (
313  int argc,
314  UNUSED ( char** argv ),
315  filter_t* filt
316 )
317 {
318  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
319  if ( argc > 1 )
320  {
321  *this << UNFILTERED << "Count filter does not take arguments" << CRLF;
322  return LIBCLI::ERROR_ANY;
323  }
324  filt->filter = &Client::count_filter;
325  filt->data = new int(0);
326  if ( ! filt->data )
327  {
328  return LIBCLI::ERROR_ANY;
329  }
330  return LIBCLI::OK;
331 }
332 
333 int
335 (
336  char* cmd,
337  void* data
338 )
339 {
340  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
341  match_filter_state* state = reinterpret_cast<match_filter_state*> ( data );
342  int r = LIBCLI::ERROR_ANY;
343  if ( !cmd ) // clean up
344  {
345  free ( state->str );
346  free ( state );
347  return LIBCLI::OK;
348  }
349  if ( strstr ( cmd, state->str ) )
350  {
351  r = LIBCLI::OK;
352  }
353  if ( state->flags & MATCH_INVERT )
354  {
355  if ( r == LIBCLI::OK )
356  {
357  r = LIBCLI::ERROR_ANY;
358  }
359  else
360  {
361  r = LIBCLI::OK;
362  }
363  }
364  return r;
365 }
366 
367 int
369 (
370  char* cmd,
371  void* data
372 )
373 {
374  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
375  range_filter_state* state = ( range_filter_state* ) data;
376  int r = LIBCLI::ERROR_ANY;
377  if ( !cmd ) // clean up
378  {
379  free_z ( state->from );
380  free_z ( state->to );
381  free_z ( state );
382  return LIBCLI::OK;
383  }
384  if ( !state->matched )
385  {
386  state->matched = !!strstr ( cmd, state->from );
387  }
388  if ( state->matched )
389  {
390  r = LIBCLI::OK;
391  if ( state->to && strstr ( cmd, state->to ) )
392  {
393  state->matched = 0;
394  }
395  }
396  return r;
397 }
398 
399 int
401 (
402  char* cmd,
403  void* data
404 )
405 {
406  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
407  int* count = ( int* ) data;
408  if ( !cmd ) // clean up
409  {
410  // print count
411  *this << UNFILTERED << NumToStr (*count, 0) << CRLF;
412  free ( count );
413  return LIBCLI::OK;
414  }
415  while ( isspace ( *cmd ) )
416  {
417  cmd++;
418  }
419  if ( *cmd )
420  {
421  ( *count ) ++; // only count non-blank lines
422  }
423  return LIBCLI::ERROR_ANY; // no output
424 }
425 
426 //////////////////////////////////////////////////////////////////////
427 //
428 //////////////////////////////////////////////////////////////////////
429 Client& commit
430 (
431  Client& out
432 )
433 {
434  filter_t* f = (out.m_print_mode & PRINT_FILTERED) ? out.filters : 0;
435  bool print = true;
436 
437  char* p = (char*) out.m_output.str().c_str();
438  while (print && f)
439  {
440  print = ( f->exec ( out, p, f->data ) == LIBCLI::OK );
441  f = f->next;
442  }
443  if (print)
444  {
445  if (out.m_socket != 0)
446  {
447  string s = out.m_output.str();
448  size_t l = s.size();
449  out.m_socket->send (s.c_str(), l);
450  }
451  else
452  {
453  cout << out.m_output.str();
454  cout.flush();
455  }
456  if (out.m_print_mode == PRINT_FILTERED)
457  { // only count lines in filtered mode
458  out.lines_out++;
459  }
460  }
461  out.m_output.str ("");
463  return out;
464 } // Client& commit()
465 //////////////////////////////////////////////////////////////////////
466 
467 //////////////////////////////////////////////////////////////////////
468 //
469 //////////////////////////////////////////////////////////////////////
470 Client& CRLF
471 (
472  Client& out
473 )
474 {
475  out.m_output << "\r\n";
476  commit (out);
477  return out;
478 } // CRLF()
479 //////////////////////////////////////////////////////////////////////
480 
481 //////////////////////////////////////////////////////////////////////
482 //
483 //////////////////////////////////////////////////////////////////////
484 Client& UNFILTERED
485 (
486  Client& out
487 )
488 {
490  return out;
491 } // CRLF()
492 //////////////////////////////////////////////////////////////////////
493 
494 }; // namespace LIBCLI
Socket type.
Definition: netSocket.h:125
#define free_z(p)
Definition: common.hxx:37
static char f[32]
Definition: crypt-win.c:225
PRINT_MODE m_print_mode
Definition: cli_client.hxx:79
#define UNUSED(d)
Definition: clitest.cxx:17
Client & commit(Client &out)
Definition: cli_client.cxx:430
int read_char(unsigned char &c)
Definition: cli_client.cxx:160
int range_filter(char *cmd, void *data)
Definition: cli_client.cxx:369
netSocket * m_socket
Definition: cli_client.hxx:80
int send(const void *buffer, int size, int flags=0)
Definition: netSocket.cxx:371
int wait_for_input(int seconds)
Definition: cli_client.cxx:125
filter_t * next
Definition: filter.hxx:41
string NumToStr(T n_Number, int n_Precision=2, int n_Base=10)
Convert a number to string.
Definition: fg_util.hxx:193
char * join_words(int argc, char **argv)
Definition: cli_client.cxx:210
int match_filter(char *cmd, void *data)
Definition: cli_client.cxx:335
filter_callback_func filter
Definition: filter.hxx:39
filter_t * filters
Definition: cli_client.hxx:70
int exec(Client &Instance, char *cmd)
Definition: filter.cxx:33
Client(int fd)
Definition: cli_client.cxx:54
size_t lines_out
Definition: cli_client.hxx:68
int range_filter_init(int argc, char **argv, filter_t *filt)
Definition: cli_client.cxx:269
Client & CRLF(Client &out)
Definition: cli_client.cxx:471
static int select(netSocket **reads, netSocket **writes, int timeout)
Definition: netSocket.cxx:489
std::ostringstream m_output
Definition: cli_client.hxx:81
Client & UNFILTERED(Client &out)
Definition: cli_client.cxx:485
void put_char(const char &c)
Definition: cli_client.cxx:178
Definition: debug.hxx:31
int count_filter(char *cmd, void *data)
Definition: cli_client.cxx:401
int match_filter_init(int argc, char **argv, filter_t *filt)
Definition: cli_client.cxx:242
int count_filter_init(int argc, char **argv, filter_t *filt)
Definition: cli_client.cxx:312