]> git.mxchange.org Git - flightgear.git/commitdiff
Bernie Bright:
authorcurt <curt>
Wed, 15 May 2002 21:44:34 +0000 (21:44 +0000)
committercurt <curt>
Wed, 15 May 2002 21:44:34 +0000 (21:44 +0000)
Here is the new super improved telnet property interface.  CVS changelog
is at the end of this message.  Once this new telnet code is in and
compiles every where we can remove Network/props.[ch]xx.  I've added a
--telnet=<port> command line option to invoke the new server.  Later on
we could remove the --props option, or least change it to invoke the new
server.  I'll let you decide.

I've added some new commands to the telnet interface:

view next      Select the next view.
view prev      Select the previous view.
view set <n>   Select view 'n'
view get       Return index of current view

I'm not sure if these same effects could be achieved through property
operations.  The commands provide a convenient shortcut in any case.

I'm also planning on adding a panel command to manipulate panels and
objects contained therein (eg simulated mouse clicks).  There is going
to be some commonality with the command objects so we may need to
rationalize this in the near future.

Finally, I've also included my python stuff.  This is still very much a
work in progress, basically I've been using it to test the new telnet
server.  I have  tested it with python 2.2.  Feel free to add it to the
repository if you want.  I would suggest a scripts/python directory

CVS Changelog

Network/telnet.cxx: New property telnet protocol interface.  It supports
the same user interface provided by the --props server.  Additionally it
handles multiple simultaneous connections.  Added "view" command to
manipulate viewmgr.

Network/protocol.hxx: Added protocol configuration exception.

Main/fg_io.cxx:  Added new "telnet" protocol.  Added protocol
configuration parse exceptions.  Simplified protocol configuration
parsing.

Main/options.cxx: Added --telnet=<port> command line option and help
message.

src/Main/fg_io.cxx
src/Main/main.cxx
src/Network/Makefile.am
src/Network/protocol.cxx
src/Network/protocol.hxx
src/Network/telnet.cxx [new file with mode: 0644]
src/Network/telnet.hxx [new file with mode: 0644]

index d98cc0769eb1884742a85ced196c9aa71be88336..e6352f0c581638e6f4c4395df7f885e629d10f15 100644 (file)
@@ -35,6 +35,7 @@
 #include <simgear/io/sg_socket_udp.hxx>
 #include <simgear/math/sg_types.hxx>
 #include <simgear/timing/timestamp.hxx>
+#include <simgear/misc/strutils.hxx>
 
 #include <Network/protocol.hxx>
 #include <Network/atc610x.hxx>
@@ -51,6 +52,7 @@
 #include <Network/opengc.hxx>
 #include <Network/nmea.hxx>
 #include <Network/props.hxx>
+#include <Network/telnet.hxx>
 #include <Network/pve.hxx>
 #include <Network/ray.hxx>
 #include <Network/rul.hxx>
@@ -68,172 +70,126 @@ io_container global_io_list;
 // configure a port based on the config string
 static FGProtocol *parse_port_config( const string& config )
 {
-    bool short_circuit = false;
-
-    string::size_type begin, end;
-
-    begin = 0;
-
     SG_LOG( SG_IO, SG_INFO, "Parse I/O channel request: " << config );
 
-    // determine protocol
-    end = config.find(",", begin);
-    if ( end == string::npos ) {
-       return NULL;            // dummy
+    vector<string> tokens = simgear::strutils::split( config, "," );
+    if (tokens.empty())
+    {
+       SG_LOG( SG_IO, SG_ALERT,
+               "Port configuration error: empty config string" );
+       return 0;
     }
-    
-    string protocol = config.substr(begin, end - begin);
-    begin = end + 1;
+
+    string protocol = tokens[0];
     SG_LOG( SG_IO, SG_INFO, "  protocol = " << protocol );
 
-    FGProtocol *io;
-    if ( protocol == "atc610x" ) {
-       FGATC610x *atc610x = new FGATC610x;
-       io = atc610x;
-        short_circuit = true;
-       cout << "here ..." << endl;
-    } else if ( protocol == "atlas" ) {
-       FGAtlas *atlas = new FGAtlas;
-       io = atlas;
-    } else if ( protocol == "opengc" ) {
-       // char wait;
-       // printf("Parsed opengc\n"); cin >> wait;
-       FGOpenGC *opengc = new FGOpenGC;
-       io = opengc;
-    } else if ( protocol == "garmin" ) {
-       FGGarmin *garmin = new FGGarmin;
-       io = garmin;
-    } else if ( protocol == "httpd" ) {
-        // determine port
-        string port = config.substr(begin);
-       FGHttpd *httpd = new FGHttpd( atoi(port.c_str()) );
-       io = httpd;
-        short_circuit = true;
+    FGProtocol *io = 0;
+
+    try
+    {
+       if ( protocol == "atc610x" ) {
+           return new FGATC610x;
+       } else if ( protocol == "atlas" ) {
+           FGAtlas *atlas = new FGAtlas;
+           io = atlas;
+       } else if ( protocol == "opengc" ) {
+           // char wait;
+           // printf("Parsed opengc\n"); cin >> wait;
+           FGOpenGC *opengc = new FGOpenGC;
+           io = opengc;
+       } else if ( protocol == "garmin" ) {
+           FGGarmin *garmin = new FGGarmin;
+           io = garmin;
+       } else if ( protocol == "httpd" ) {
+           // determine port
+           string port = tokens[1];
+           return new FGHttpd( atoi(port.c_str()) );
 #ifdef FG_JPEG_SERVER
-    } else if ( protocol == "jpg-httpd" ) {
-        // determine port
-        string port = config.substr(begin);
-       FGJpegHttpd *jpeg_httpd = new FGJpegHttpd( atoi(port.c_str()) );
-       io = jpeg_httpd;
-        short_circuit = true;
+       } else if ( protocol == "jpg-httpd" ) {
+           // determine port
+           string port = tokens[1];
+           return new FGJpegHttpd( atoi(port.c_str()) );
 #endif
-    } else if ( protocol == "joyclient" ) {
-       FGJoyClient *joyclient = new FGJoyClient;
-       io = joyclient;
-    } else if ( protocol == "native" ) {
-       FGNative *native = new FGNative;
-       io = native;
-    } else if ( protocol == "native_ctrls" ) {
-       FGNativeCtrls *native_ctrls = new FGNativeCtrls;
-       io = native_ctrls;
-    } else if ( protocol == "native_fdm" ) {
-       FGNativeFDM *native_fdm = new FGNativeFDM;
-       io = native_fdm;
-    } else if ( protocol == "nmea" ) {
-       FGNMEA *nmea = new FGNMEA;
-       io = nmea;
-    } else if ( protocol == "props" ) {
-       FGProps *props = new FGProps;
-       io = props;
-    } else if ( protocol == "pve" ) {
-       FGPVE *pve = new FGPVE;
-       io = pve;
-    } else if ( protocol == "ray" ) {
-       FGRAY *ray = new FGRAY;
-       io = ray;
-    } else if ( protocol == "rul" ) {
-       FGRUL *rul = new FGRUL;
-       io = rul;
-    } else {
-       return NULL;
+       } else if ( protocol == "joyclient" ) {
+           FGJoyClient *joyclient = new FGJoyClient;
+           io = joyclient;
+       } else if ( protocol == "native" ) {
+           FGNative *native = new FGNative;
+           io = native;
+       } else if ( protocol == "native_ctrls" ) {
+           FGNativeCtrls *native_ctrls = new FGNativeCtrls;
+           io = native_ctrls;
+       } else if ( protocol == "native_fdm" ) {
+           FGNativeFDM *native_fdm = new FGNativeFDM;
+           io = native_fdm;
+       } else if ( protocol == "nmea" ) {
+           FGNMEA *nmea = new FGNMEA;
+           io = nmea;
+       } else if ( protocol == "props" ) {
+           io = new FGProps();
+       } else if ( protocol == "telnet" ) {
+           io = new FGTelnet( tokens );
+           return io;
+       } else if ( protocol == "pve" ) {
+           FGPVE *pve = new FGPVE;
+           io = pve;
+       } else if ( protocol == "ray" ) {
+           FGRAY *ray = new FGRAY;
+           io = ray;
+       } else if ( protocol == "rul" ) {
+           FGRUL *rul = new FGRUL;
+           io = rul;
+       } else {
+           return NULL;
+       }
+    }
+    catch (FGProtocolConfigError& err)
+    {
+       SG_LOG( SG_IO, SG_ALERT, "Port configuration error: " << err.what() );
+       delete io;
+       return 0;
     }
 
-    if ( ! short_circuit ) {
-        // determine medium
-        end = config.find(",", begin);
-        if ( end == string::npos ) {
-            return NULL;               // dummy
-        }
-    
-        string medium = config.substr(begin, end - begin);
-        begin = end + 1;
-        SG_LOG( SG_IO, SG_INFO, "  medium = " << medium );
-
-        // determine direction
-        end = config.find(",", begin);
-        if ( end == string::npos ) {
-            return NULL;               // dummy
-        }
-    
-        string direction = config.substr(begin, end - begin);
-        begin = end + 1;
-        io->set_direction( direction );
-        SG_LOG( SG_IO, SG_INFO, "  direction = " << direction );
-
-        // determine hertz
-        end = config.find(",", begin);
-        if ( end == string::npos ) {
-            return NULL;               // dummy
-        }
-    
-        string hertz_str = config.substr(begin, end - begin);
-        begin = end + 1;
-        double hertz = atof( hertz_str.c_str() );
-        io->set_hz( hertz );
-        SG_LOG( SG_IO, SG_INFO, "  hertz = " << hertz );
-
-        if ( medium == "serial" ) {
-            // device name
-            end = config.find(",", begin);
-            if ( end == string::npos ) {
-                return NULL;
-            }
-    
-            string device = config.substr(begin, end - begin);
-            begin = end + 1;
-            SG_LOG( SG_IO, SG_INFO, "  device = " << device );
-
-            // baud
-            string baud = config.substr(begin);
-            SG_LOG( SG_IO, SG_INFO, "  baud = " << baud );
-
-            SGSerial *ch = new SGSerial( device, baud );
-            io->set_io_channel( ch );
-        } else if ( medium == "file" ) {
-            // file name
-            string file = config.substr(begin);
-            SG_LOG( SG_IO, SG_INFO, "  file name = " << file );
-
-            SGFile *ch = new SGFile( file );
-            io->set_io_channel( ch );
-        } else if ( medium == "socket" ) {
-            // hostname
-            end = config.find(",", begin);
-            if ( end == string::npos ) {
-                return NULL;
-            }
-    
-            string hostname = config.substr(begin, end - begin);
-            begin = end + 1;
-            SG_LOG( SG_IO, SG_INFO, "  hostname = " << hostname );
-
-            // port string
-            end = config.find(",", begin);
-            if ( end == string::npos ) {
-                return NULL;
-            }
-    
-            string port = config.substr(begin, end - begin);
-            begin = end + 1;
-            SG_LOG( SG_IO, SG_INFO, "  port string = " << port );
-
-            // socket style
-            string style_str = config.substr(begin);
-            SG_LOG( SG_IO, SG_INFO, "  style string = " << style_str );
+    string medium = tokens[1];
+    SG_LOG( SG_IO, SG_INFO, "  medium = " << medium );
+
+    string direction = tokens[2];
+    io->set_direction( direction );
+    SG_LOG( SG_IO, SG_INFO, "  direction = " << direction );
+
+    string hertz_str = tokens[3];
+    double hertz = atof( hertz_str.c_str() );
+    io->set_hz( hertz );
+    SG_LOG( SG_IO, SG_INFO, "  hertz = " << hertz );
+
+    if ( medium == "serial" ) {
+       // device name
+       string device = tokens[4];
+       SG_LOG( SG_IO, SG_INFO, "  device = " << device );
+
+       // baud
+       string baud = tokens[5];
+       SG_LOG( SG_IO, SG_INFO, "  baud = " << baud );
+
+       SGSerial *ch = new SGSerial( device, baud );
+       io->set_io_channel( ch );
+    } else if ( medium == "file" ) {
+       // file name
+       string file = tokens[4];
+       SG_LOG( SG_IO, SG_INFO, "  file name = " << file );
+
+       SGFile *ch = new SGFile( file );
+       io->set_io_channel( ch );
+    } else if ( medium == "socket" ) {
+       string hostname = tokens[4];
+       string port = tokens[5];
+       string style = tokens[6];
+
+       SG_LOG( SG_IO, SG_INFO, "  hostname = " << hostname );
+       SG_LOG( SG_IO, SG_INFO, "  port = " << port );
+       SG_LOG( SG_IO, SG_INFO, "  style = " << style );
             
-            SGSocket *ch = new SGSocket( hostname, port, style_str );
-            io->set_io_channel( ch );
-        }
+       io->set_io_channel( new SGSocket( hostname, port, style ) );
     }
 
     return io;
@@ -273,8 +229,6 @@ void fgIOInit() {
 
 // process any serial port work
 void fgIOProcess() {
-    FGProtocol *p;
-
     // cout << "processing I/O channels" << endl;
 
     static int inited = 0;
@@ -292,9 +246,9 @@ void fgIOProcess() {
        last = current;
     }
 
-    for ( int i = 0; i < (int)global_io_list.size(); ++i ) {
+    for ( unsigned int i = 0; i < global_io_list.size(); ++i ) {
        // cout << "  channel = " << i << endl;
-       p = global_io_list[i];
+       FGProtocol* p = global_io_list[i];
 
        if ( p->is_enabled() ) {
            p->dec_count_down( interval );
index dc0ec237fe3e11c891b4265d001400cc7825cb25..11e2ef141c8587e5cfb86b9c869b31467712dafe 100644 (file)
@@ -1332,7 +1332,8 @@ int mainLoop( int argc, char **argv ) {
     version = "unknown version";
 #endif
     SG_LOG( SG_GENERAL, SG_INFO, "FlightGear:  Version "
-           << version << endl );
+           << version );
+    SG_LOG( SG_GENERAL, SG_INFO, "Built with " << SG_COMPILER_STR );
 
     // Allocate global data structures.  This needs to happen before
     // we parse command line options
index 7766c0977e1b7f4799f1d64d07ec7f34749504f2..de3c00fde4ba4297ae4d6aa4973efc3ab4dc9c41 100644 (file)
@@ -24,7 +24,8 @@ libNetwork_a_SOURCES = \
        pve.cxx pve.hxx \
        raw_ctrls.hxx \
        ray.cxx ray.hxx \
-       rul.cxx rul.hxx
+       rul.cxx rul.hxx \
+       telnet.cxx telnet.hxx
 
 if OLD_AUTOMAKE
 INCLUDES += -I$(top_srcdir) -I$(top_srcdir)/src
index ea1e66678123bebd40ed28c96d3372a28acf8b5f..be4883ecfd09db29b692ed75f6d37c8abf17eaf2 100644 (file)
@@ -95,3 +95,14 @@ bool FGProtocol::parse_message() {
 }
 
 
+void FGProtocol::set_direction( const string& d ) {
+    if ( d == "in" ) {
+       dir = SG_IO_IN;
+    } else if ( d == "out" ) {
+       dir = SG_IO_OUT;
+    } else if ( d == "bi" ) {
+       dir = SG_IO_BI;
+    } else {
+       dir = SG_IO_NONE;
+    }
+}
index 8c6535b520448d5bfe14c8832265595336616801..4b73d18935a2be1de74c35fec3f20dac75ca4196 100644 (file)
@@ -26,6 +26,7 @@
 
 
 #include <simgear/compiler.h>
+#include <simgear/io/iochannel.hxx>
 
 #include STL_STRING
 #include <vector>
@@ -36,10 +37,6 @@ SG_USING_STD(vector);
 
 #define FG_MAX_MSG_SIZE 16384
 
-// forward declaration
-class SGIOChannel;
-
-
 class FGProtocol {
 
 private:
@@ -68,17 +65,7 @@ public:
     virtual bool close();
 
     inline SGProtocolDir get_direction() const { return dir; }
-    inline void set_direction( const string& d ) {
-       if ( d == "in" ) {
-           dir = SG_IO_IN;
-       } else if ( d == "out" ) {
-           dir = SG_IO_OUT;
-       } else if ( d == "bi" ) {
-           dir = SG_IO_BI;
-       } else {
-           dir = SG_IO_NONE;
-       }
-    }
+    void set_direction( const string& d );
 
     inline double get_hz() const { return hz; }
     inline void set_hz( double t ) { hz = t; }
@@ -108,6 +95,18 @@ typedef vector < FGProtocol * > io_container;
 typedef io_container::iterator io_iterator;
 typedef io_container::const_iterator const_io_iterator;
 
+#include <stdexcept>
+SG_USING_STD(invalid_argument);
+
+//namespace flightgear { namespace network {
+class FGProtocolConfigError : public invalid_argument
+{
+public:
+    FGProtocolConfigError( const string& what_string )
+       : invalid_argument(what_string) {}
+};
+//}} // end namespace flightgear::network
+
 
 #endif // _PROTOCOL_HXX
 
diff --git a/src/Network/telnet.cxx b/src/Network/telnet.cxx
new file mode 100644 (file)
index 0000000..8af97db
--- /dev/null
@@ -0,0 +1,547 @@
+// \file telnet.cx
+// Property telnet server class.
+//
+// Written by Bernie Bright, started May 2002.
+//
+// Copyright (C) 2002  Bernie Bright - bbright@bigpond.net.au
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <simgear/compiler.h>
+#include <simgear/debug/logstream.hxx>
+#include <simgear/misc/strutils.hxx>
+#include <simgear/misc/props.hxx>
+#include <simgear/misc/props_io.hxx>
+
+#include STL_STRSTREAM
+
+#include <Main/globals.hxx>
+#include <Main/viewmgr.hxx>
+
+#include <plib/netChat.h>
+
+#include "telnet.hxx"
+
+#if !defined(SG_HAVE_NATIVE_SGI_COMPILERS)
+SG_USING_STD(strstream);
+#endif
+
+/**
+ * Telnet connection class.
+ * This class represents a connection to a telnet-style client.
+ */
+class TelnetChannel : public netChat
+{
+    netBuffer buffer;
+
+    /**
+     * Current property node name.
+     */
+    string path;
+
+    enum Mode {
+       PROMPT,
+       DATA
+    };
+    Mode mode;
+
+public:
+    /**
+     * Constructor.
+     */
+    TelnetChannel();
+    
+    /**
+     * Append incoming data to our request buffer.
+     *
+     * @param s Character string to append to buffer
+     * @param n Number of characters to append.
+     */
+    void collectIncomingData( const char* s, int n );
+
+    /**
+     * Process a complete request from the telnet client.
+     */
+    void foundTerminator();
+
+private:
+    /**
+     * Return a "Node no found" error message to the client.
+     */
+    void node_not_found_error( const string& node_name );
+
+    void view_cmd( const vector<string>& );
+};
+
+/**
+ * 
+ */
+TelnetChannel::TelnetChannel()
+    : buffer(512),
+      path("/"),
+      mode(PROMPT)
+{
+    setTerminator( "\r\n" );
+}
+
+/**
+ * 
+ */
+void
+TelnetChannel::collectIncomingData( const char* s, int n )
+{
+    buffer.append( s, n );
+}
+
+/**
+ * 
+ */
+void
+TelnetChannel::node_not_found_error( const string& node_name )
+{
+    string error = "ERR Node \"";
+    error += node_name;
+    error += "\" not found.";
+    push( error.c_str() );
+    push( getTerminator() );
+}
+
+// return a human readable form of the value "type"
+static string
+getValueTypeString( const SGPropertyNode *node )
+{
+    string result;
+
+    if ( node == NULL )
+    {
+       return "unknown";
+    }
+
+    SGPropertyNode::Type type = node->getType();
+    if ( type == SGPropertyNode::UNSPECIFIED ) {
+       result = "unspecified";
+    } else if ( type == SGPropertyNode::NONE ) {
+        result = "none";
+    } else if ( type == SGPropertyNode::BOOL ) {
+       result = "bool";
+    } else if ( type == SGPropertyNode::INT ) {
+       result = "int";
+    } else if ( type == SGPropertyNode::LONG ) {
+       result = "long";
+    } else if ( type == SGPropertyNode::FLOAT ) {
+       result = "float";
+    } else if ( type == SGPropertyNode::DOUBLE ) {
+       result = "double";
+    } else if ( type == SGPropertyNode::STRING ) {
+       result = "string";
+    }
+
+    return result;
+}
+
+/**
+ * We have a command.
+ * 
+ * TODO: possible future commands:
+ * panel <subcmd>
+ *   panel load [path]
+ *   panel mouse <button> up|down|click <x> <y>
+ *   panel visible 0|1
+ *   panel height -> h, Retrieve panel height
+ *   panel width -> w, Retrieve panel width
+ *   panel xoffset -> x, Retrieve panel x offset
+ *   panel yoffset -> y, Retrieve panel y offset
+ *
+ * property <subcmd>
+ *   property toggle <prop>
+ *   property adjust <prop> <step> <offset> <factor> <min> <max> <wrap>
+ *   property multiply <prop> <factor>
+ *   property swap <prop1> <prop2>
+ *   property scale <prop> <setting> <offset> <factor>
+ *
+ * view <subcmd>
+ *   view next
+ *   view prev
+ *   view set <n>
+ *   view current -> n, Retrieve index of current view
+ */
+void
+TelnetChannel::foundTerminator()
+{
+    const char* cmd = buffer.getData();
+    SG_LOG( SG_IO, SG_INFO, "processing command = \"" << cmd << "\"" );
+
+    vector<string> tokens = simgear::strutils::split( cmd );
+
+    SGPropertyNode* node = globals->get_props()->getNode( path.c_str() );
+
+    if (!tokens.empty())
+    {
+       string command = tokens[0];
+
+       if (command == "ls")
+       {
+           SGPropertyNode* dir = node;
+           if (tokens.size() == 2)
+           {
+               if (tokens[1][0] == '/')
+               {
+                   dir = globals->get_props()->getNode( tokens[1].c_str() );
+               }
+               else
+               {
+                   string s = path;
+                   s += "/";
+                   s += tokens[1];
+                   dir = globals->get_props()->getNode( s.c_str() );
+               }
+
+               if (dir == 0)
+               {
+                   node_not_found_error( tokens[1] );
+                   goto prompt;
+               }
+           }
+
+           for (int i = 0; i < dir->nChildren(); i++)
+           {
+               SGPropertyNode * child = dir->getChild(i);
+               string name = child->getName();
+               string line = name;
+
+               if (dir->getChild( name.c_str(), 1 ))
+               {
+                   char buf[16];
+                   sprintf(buf, "[%d]", child->getIndex());
+                   line += buf;
+               }
+
+               if ( child->nChildren() > 0 )
+               {
+                   line += "/";
+               }
+               else
+               {
+                   if (mode == PROMPT)
+                   {
+                       string value = dir->getStringValue( name.c_str(), "" );
+                       line += " =\t'" + value + "'\t(";
+                       line += getValueTypeString(
+                                       dir->getNode( name.c_str() ) );
+                       line += ")";
+                   }
+               }
+
+               line += getTerminator();
+               push( line.c_str() );
+           }
+       }
+       else if ( command == "dump" )
+       {
+           strstream buf;
+           if ( tokens.size() <= 1 )
+           {
+               writeProperties( buf, node );
+               push( buf.str() );
+               push( getTerminator() );
+           }
+           else
+           {
+               SGPropertyNode *child = node->getNode( tokens[1].c_str() );
+               if ( child )
+               {
+                   writeProperties ( buf, child );
+                   push( buf.str() );
+                   push( getTerminator() );
+               }
+               else
+               {
+                   node_not_found_error( tokens[1] );
+               }
+           }
+       }
+       else if ( command == "cd" )
+       {
+           if (tokens.size() == 2)
+           {
+               try
+               {
+                   SGPropertyNode* child = node->getNode( tokens[1].c_str() );
+                   if ( child )
+                   {
+                       node = child;
+                       path = node->getPath();
+                   }
+                   else
+                   {
+                       node_not_found_error( tokens[1] );
+                   }
+               }
+               catch (...)
+               {
+                   // Ignore attempt to move past root node with ".."
+               }
+           }
+       }
+       else if ( command == "pwd" )
+       {
+           string ttt = node->getPath();
+           if (ttt.empty())
+           {
+               ttt = "/";
+           }
+
+           push( ttt.c_str() );
+           push( getTerminator() );
+       }
+       else if ( command == "get" || command == "show" )
+       {
+           if ( tokens.size() == 2 )
+           {
+               string tmp;     
+               string value = node->getStringValue ( tokens[1].c_str(), "" );
+               if ( mode == PROMPT )
+               {
+                   tmp = tokens[1];
+                   tmp += " = '";
+                   tmp += value;
+                   tmp += "' (";
+                   tmp += getValueTypeString(
+                                    node->getNode( tokens[1].c_str() ) );
+                   tmp += ")\n";
+               }
+               else
+               {
+                   tmp = value + "\n";
+               }
+               push( tmp.c_str() );
+           }
+       }
+       else if ( command == "set" )
+       {
+           if ( tokens.size() == 3 )
+           {
+               node->getNode( tokens[1].c_str(), true )->setStringValue(tokens[2].c_str());
+
+               if ( mode == PROMPT )
+               {
+                   // now fetch and write out the new value as confirmation
+                   // of the change
+                   string value = node->getStringValue ( tokens[1].c_str(), "" );
+                   string tmp = tokens[1] + " = '" + value + "' (";
+                   tmp += getValueTypeString( node->getNode( tokens[1].c_str() ) );
+                   tmp += ")\n";
+                   push( tmp.c_str() );
+               }
+           }
+       }
+       else if (command == "quit")
+       {
+           close();
+           shouldDelete();
+           return;
+       }
+       else if ( command == "data" )
+       {
+           mode = DATA;
+       }
+       else if ( command == "prompt" )
+       {
+           mode = PROMPT;
+       }
+       else if ( command == "view" )
+       {
+           view_cmd( tokens );
+       }
+//     else if ( command == "panel" )
+//     {
+//         panel_cmd( tokens );
+//     }
+//     else if ( command == "property" )
+//     {
+//         property_cmd( tokens );
+//     }
+       else
+       {
+           const char* msg = "
+Valid commands are:
+
+cd <dir>         cd to a directory, '..' to move back
+data             switch to raw data mode
+dump             dump current state (in xml)
+get <var>        show the value of a parameter
+help             show this help message
+ls [<dir>]       list directory
+prompt           switch to interactive mode (default)
+pwd              display your current path
+quit             terminate connection
+set <var> <val>  set <var> to a new <val>
+show <var>       synonym for get
+view next        display next view
+view prev        display prev view
+view set <n>     display view 'n'
+view get         return current view index
+view current     return current view index
+";
+           push( msg );
+       }
+    }
+
+ prompt:
+    if (mode == PROMPT)
+    {
+       string prompt = node->getPath();
+       if (prompt.empty())
+       {
+           prompt = "/";
+       }
+       prompt += "> ";
+       push( prompt.c_str() );
+    }
+
+    buffer.remove();
+}
+
+/**
+ * 
+ */
+void
+TelnetChannel::view_cmd( const vector<string>& tokens )
+{
+    if (tokens.size() <= 1)
+    {
+       // ERROR: no sub-command
+       return;
+    }
+    string subcmd = tokens[1];
+
+    if (subcmd == "next")
+    {
+       globals->get_current_view()->setHeadingOffset_deg(0.0);
+       globals->get_viewmgr()->next_view();
+    }
+    else if (subcmd == "prev")
+    {
+       globals->get_current_view()->setHeadingOffset_deg(0.0);
+       globals->get_viewmgr()->prev_view();
+    }
+    else if (subcmd == "set")
+    {
+       if (tokens.size() == 3)
+       {
+           int i = atoi( tokens[2].c_str() );
+           if (0 >= i && i < globals->get_viewmgr()->size())
+           {
+               globals->get_current_view()->setHeadingOffset_deg(0.0);
+               globals->get_viewmgr()->set_view(i);
+               globals->get_viewmgr()->copyToCurrent();
+           }
+       }
+    }
+    else if (subcmd == "get" || subcmd == "current")
+    {
+       int i = globals->get_viewmgr()->get_current();
+       char buf[16];
+       snprintf( buf, sizeof(buf), "%d", i );
+       push( buf );
+       push( getTerminator() );
+    }
+    else
+    {
+       // ERROR: invalid subcommand.
+    }
+}
+
+/**
+ * 
+ */
+FGTelnet::FGTelnet( const vector<string>& tokens )
+{
+    if (tokens.size() != 2)
+    {
+       throw FGProtocolConfigError( "FGProps: expected 1 argument, <port>" );
+    }
+
+    port = atoi( tokens[1].c_str() );
+}
+
+/**
+ * 
+ */
+FGTelnet::~FGTelnet()
+{
+}
+
+/**
+ * 
+ */
+bool
+FGTelnet::open()
+{
+    if ( is_enabled() )
+    {
+       SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " 
+               << "is already in use, ignoring" );
+       return false;
+    }
+
+    netChannel::open();
+    netChannel::bind( "", port );
+    netChannel::listen( 5 );
+    SG_LOG( SG_IO, SG_INFO, "Telnet server started on port " << port );
+
+    set_hz( 5 );                // default to processing requests @ 5Hz
+    set_enabled( true );
+    return true;
+}
+
+/**
+ * 
+ */
+bool
+FGTelnet::close()
+{
+    return true;
+}
+
+/**
+ * 
+ */
+bool
+FGTelnet::process()
+{
+    netChannel::poll();
+    return true;
+}
+
+/**
+ * 
+ */
+void
+FGTelnet::handleAccept()
+{
+    netAddress addr;
+    int handle = accept( &addr );
+    SG_LOG( SG_IO, SG_INFO, "Telnet server accepted connection from "
+           << addr.getHost() << ":" << addr.getPort() );
+    TelnetChannel* channel = new TelnetChannel();
+    channel->setHandle( handle );
+}
diff --git a/src/Network/telnet.hxx b/src/Network/telnet.hxx
new file mode 100644 (file)
index 0000000..3d7bf2f
--- /dev/null
@@ -0,0 +1,89 @@
+// \file telnet.hxx
+// Property server class.
+//
+// Written by Bernie Bright, started May 2002.
+//
+// Copyright (C) 2002  Bernie Bright - bbright@bigpond.net.au
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#ifndef TELNET_HXX_INCLUDED
+#define TELNET_HXX_INCLUDED 1
+
+#include <simgear/compiler.h>
+#include STL_STRING
+#include <vector>
+
+SG_USING_STD(string);
+SG_USING_STD(vector);
+
+#include <plib/netChannel.h>
+
+#include "protocol.hxx"
+
+/**
+ * Property server class.
+ * This class provides a telnet-like server for remote access to
+ * FlightGear properties.
+ */
+class FGTelnet : public FGProtocol,
+                public netChannel
+{
+private:
+
+    /**
+     * Server port to listen on.
+     */
+    int port;
+
+public:
+    /**
+     * Create a new TCP server.
+     * 
+     * @param tokens Tokenized configuration parameters
+     */
+    FGTelnet( const vector<string>& tokens );
+
+    /**
+     * Destructor.
+     */
+    ~FGTelnet();
+
+    /**
+     * Start the telnet server.
+     */
+    bool open();
+
+    /**
+     * Process network activity.
+     */
+    bool process();
+
+    /**
+     * 
+     */
+    bool close();
+
+    /**
+     * Accept a new client connection.
+     */
+    void handleAccept();
+
+};
+
+#endif //TELNET_HXX_INCLUDED
+