]> git.mxchange.org Git - flightgear.git/blobdiff - src/Main/options.cxx
Hopefully fix the shadow disappearing because of range animation issue in a more...
[flightgear.git] / src / Main / options.cxx
index 8041e67df6dc046781055f77c111836fe8ade276..78ec58d87138524195ee0ebcafb7e92de92fa8d6 100644 (file)
@@ -31,6 +31,8 @@
 #include <simgear/timing/sg_time.hxx>
 #include <simgear/misc/sg_dir.hxx>
 
+#include <boost/foreach.hpp>
+
 #include <math.h>              // rint()
 #include <stdio.h>
 #include <stdlib.h>            // atof(), atoi()
@@ -46,6 +48,7 @@
 #include <simgear/misc/sg_path.hxx>
 #include <simgear/scene/material/mat.hxx>
 #include <simgear/sound/soundmgr_openal.hxx>
+#include <simgear/misc/strutils.hxx>
 #include <Autopilot/route_mgr.hxx>
 #include <GUI/gui.h>
 
 #include "util.hxx"
 #include "viewmgr.hxx"
 #include <Main/viewer.hxx>
+#include <Environment/presets.hxx>
 
-#include <simgear/version.h>
 #include <osg/Version>
 
-using std::string;
-using std::sort;
-using std::cout;
-using std::cerr;
-using std::endl;
-
 #if defined( HAVE_VERSION_H ) && HAVE_VERSION_H
 #  include <Include/version.h>
+#  include <simgear/version.h>
 #else
 #  include <Include/no_version.h>
 #endif
 
+#ifdef __APPLE__
+#  include <CoreFoundation/CoreFoundation.h>
+#endif
+
+using std::string;
+using std::sort;
+using std::cout;
+using std::cerr;
+using std::endl;
+using std::vector;
+using std::cin;
+
 #define NEW_DEFAULT_MODEL_HZ 120
 
+// defined in bootstrap.cxx
+extern char *homedir;
+extern char *hostname;
+
 enum
 {
     FG_OPTIONS_OK = 0,
@@ -85,6 +99,8 @@ enum
     FG_OPTIONS_SHOW_SOUND_DEVICES = 6
 };
 
+static flightgear::Options* shared_instance = NULL;
+
 static double
 atof( const string& str )
 {
@@ -97,6 +113,8 @@ atoi( const string& str )
     return ::atoi( str.c_str() );
 }
 
+static int fgSetupProxy( const char *arg );
+
 /**
  * Set a few fail-safe default property values.
  *
@@ -104,21 +122,10 @@ atoi( const string& str )
  * in case, we provide some initial sane values here. This method
  * should be invoked *before* reading any init files.
  */
-void
+static void
 fgSetDefaults ()
 {
-    // set a possibly independent location for scenery data
-    const char *envp = ::getenv( "FG_SCENERY" );
 
-    if ( envp != NULL ) {
-       // fg_root could be anywhere, so default to environmental
-       // variable $FG_ROOT if it is set.
-        globals->set_fg_scenery(envp);
-    } else {
-       // Otherwise, default to Scenery being in $FG_ROOT/Scenery
-       globals->set_fg_scenery("");
-    }
-        
                                // Position (deliberately out of range)
     fgSetDouble("/position/longitude-deg", 9999.0);
     fgSetDouble("/position/latitude-deg", 9999.0);
@@ -169,7 +176,7 @@ fgSetDefaults ()
     // specified so we can do the right thing for voodoo-1/2 cards.
     // fgSetString("/sim/startup/mouse-pointer", "disabled");
     fgSetString("/sim/control-mode", "joystick");
-    fgSetBool("/sim/auto-coordination", false);
+    fgSetBool("/controls/flight/auto-coordination", false);
 #if defined(WIN32)
     fgSetString("/sim/startup/browser-app", "webrun.bat");
 #elif defined(__APPLE__)
@@ -177,16 +184,16 @@ fgSetDefaults ()
 #elif defined(sgi)
     fgSetString("/sim/startup/browser-app", "launchWebJumper");
 #else
-    envp = ::getenv( "WEBBROWSER" );
-    if (!envp) envp = "netscape";
-    fgSetString("/sim/startup/browser-app", envp);
+    const char* browserEnv = ::getenv( "WEBBROWSER" );
+    if (!browserEnv) browserEnv = "netscape";
+    fgSetString("/sim/startup/browser-app", browserEnv);
 #endif
     fgSetString("/sim/logging/priority", "alert");
 
                                // Features
     fgSetBool("/sim/hud/color/antialiased", false);
-    fgSetBool("/sim/hud/enable3d", true);
-    fgSetBool("/sim/hud/visibility", false);
+    fgSetBool("/sim/hud/enable3d[1]", true);
+    fgSetBool("/sim/hud/visibility[1]", false);
     fgSetBool("/sim/panel/visibility", true);
     fgSetBool("/sim/sound/enabled", true);
     fgSetBool("/sim/sound/working", true);
@@ -211,6 +218,7 @@ fgSetDefaults ()
     fgSetBool("/sim/rendering/enhanced-lighting", false);
     fgSetBool("/sim/rendering/distance-attenuation", false);
     fgSetBool("/sim/rendering/specular-highlight", true);
+    fgSetString("/sim/rendering/materials-file", "materials.xml");
     fgSetInt("/sim/startup/xsize", 800);
     fgSetInt("/sim/startup/ysize", 600);
     fgSetInt("/sim/rendering/bits-per-pixel", 16);
@@ -220,7 +228,7 @@ fgSetDefaults ()
                                // HUD options
     fgSetString("/sim/startup/units", "feet");
     fgSetString("/sim/hud/frame-stat-type", "tris");
-       
+
                                // Time options
     fgSetInt("/sim/startup/time-offset", 0);
     fgSetString("/sim/startup/time-offset-type", "system-offset");
@@ -233,8 +241,8 @@ fgSetDefaults ()
     fgSetBool("/sim/freeze/fuel", false);
 
     fgSetString("/sim/multiplay/callsign", "callsign");
-    fgSetString("/sim/multiplay/rxhost", "0");
-    fgSetString("/sim/multiplay/txhost", "0");
+    fgSetString("/sim/multiplay/rxhost", "");
+    fgSetString("/sim/multiplay/txhost", "");
     fgSetInt("/sim/multiplay/rxport", 0);
     fgSetInt("/sim/multiplay/txport", 0);
     
@@ -244,6 +252,10 @@ fgSetDefaults ()
     fgSetString("/sim/version/revision", REVISION);
     fgSetInt("/sim/version/build-number", HUDSON_BUILD_NUMBER);
     fgSetString("/sim/version/build-id", HUDSON_BUILD_ID);
+  
+  char* envp = ::getenv( "http_proxy" );
+    if( envp != NULL )
+      fgSetupProxy( envp );
 }
 
 static bool
@@ -272,6 +284,56 @@ parse_wind (const string &wind, double * min_hdg, double * max_hdg,
   return true;
 }
 
+static bool
+parseIntValue(char** ppParserPos, int* pValue,int min, int max, const char* field, const char* argument)
+{
+    if ( !strlen(*ppParserPos) )
+        return true;
+
+    char num[256];
+    int i = 0;
+
+    while ( isdigit((*ppParserPos)[0]) && (i<255) )
+    {
+        num[i] = (*ppParserPos)[0];
+        (*ppParserPos)++;
+        i++;
+    }
+    num[i] = '\0';
+
+    switch ((*ppParserPos)[0])
+    {
+        case 0:
+            break;
+        case ':':
+            (*ppParserPos)++;
+            break;
+        default:
+            SG_LOG(SG_GENERAL, SG_ALERT, "Illegal character in time string for " << field << ": '" <<
+                    (*ppParserPos)[0] << "'.");
+            // invalid field - skip rest of string to avoid further errors
+            while ((*ppParserPos)[0])
+                (*ppParserPos)++;
+            return false;
+    }
+
+    if (i<=0)
+        return true;
+
+    int value = atoi(num);
+    if ((value < min)||(value > max))
+    {
+        SG_LOG(SG_GENERAL, SG_ALERT, "Invalid " << field << " in '" << argument <<
+               "'. Valid range is " << min << "-" << max << ".");
+        return false;
+    }
+    else
+    {
+        *pValue = value;
+        return true;
+    }
+}
+
 // parse a time string ([+/-]%f[:%f[:%f]]) into hours
 static double
 parse_time(const string& time_in) {
@@ -353,112 +415,63 @@ parse_time(const string& time_in) {
     return(sign * result);
 }
 
-
 // parse a date string (yyyy:mm:dd:hh:mm:ss) into a time_t (seconds)
-static long int 
-parse_date( const string& date)
+static long int
+parse_date( const string& date, const char* timeType)
 {
-    struct tm gmt;
-    char * date_str, num[256];
-    int i;
-    // initialize to zero
-    gmt.tm_sec = 0;
-    gmt.tm_min = 0;
-    gmt.tm_hour = 0;
-    gmt.tm_mday = 0;
-    gmt.tm_mon = 0;
-    gmt.tm_year = 0;
-    gmt.tm_isdst = 0; // ignore daylight savings time for the moment
-    date_str = (char *)date.c_str();
-    // get year
-    if ( strlen(date_str) ) {
-       i = 0;
-       while ( (date_str[0] != ':') && (date_str[0] != '\0') ) {
-           num[i] = date_str[0];
-           date_str++;
-           i++;
-       }
-       if ( date_str[0] == ':' ) {
-           date_str++;
-       }
-       num[i] = '\0';
-       gmt.tm_year = atoi(num) - 1900;
-    }
-    // get month
-    if ( strlen(date_str) ) {
-       i = 0;
-       while ( (date_str[0] != ':') && (date_str[0] != '\0') ) {
-           num[i] = date_str[0];
-           date_str++;
-           i++;
-       }
-       if ( date_str[0] == ':' ) {
-           date_str++;
-       }
-       num[i] = '\0';
-       gmt.tm_mon = atoi(num) -1;
-    }
-    // get day
-    if ( strlen(date_str) ) {
-       i = 0;
-       while ( (date_str[0] != ':') && (date_str[0] != '\0') ) {
-           num[i] = date_str[0];
-           date_str++;
-           i++;
-       }
-       if ( date_str[0] == ':' ) {
-           date_str++;
-       }
-       num[i] = '\0';
-       gmt.tm_mday = atoi(num);
-    }
-    // get hour
-    if ( strlen(date_str) ) {
-       i = 0;
-       while ( (date_str[0] != ':') && (date_str[0] != '\0') ) {
-           num[i] = date_str[0];
-           date_str++;
-           i++;
-       }
-       if ( date_str[0] == ':' ) {
-           date_str++;
-       }
-       num[i] = '\0';
-       gmt.tm_hour = atoi(num);
-    }
-    // get minute
-    if ( strlen(date_str) ) {
-       i = 0;
-       while ( (date_str[0] != ':') && (date_str[0] != '\0') ) {
-           num[i] = date_str[0];
-           date_str++;
-           i++;
-       }
-       if ( date_str[0] == ':' ) {
-           date_str++;
-       }
-       num[i] = '\0';
-       gmt.tm_min = atoi(num);
+    struct tm gmt,*pCurrentTime;
+    int year,month,day,hour,minute,second;
+    char *argument, *date_str;
+
+    SGTime CurrentTime = SGTime();
+    CurrentTime.update(0,0,0,0);
+
+    // FIXME This should obtain system/aircraft/GMT time depending on timeType
+    pCurrentTime = CurrentTime.getGmt();
+
+    // initialize all fields with current time
+    year   = pCurrentTime->tm_year + 1900;
+    month  = pCurrentTime->tm_mon + 1;
+    day    = pCurrentTime->tm_mday;
+    hour   = pCurrentTime->tm_hour;
+    minute = pCurrentTime->tm_min;
+    second = pCurrentTime->tm_sec;
+
+    argument = (char *)date.c_str();
+    date_str = argument;
+
+    // start with parsing year
+    if (!strlen(date_str) ||
+        !parseIntValue(&date_str,&year,0,9999,"year",argument))
+    {
+        return -1;
     }
-    // get second
-    if ( strlen(date_str) ) {
-       i = 0;
-       while ( (date_str[0] != ':') && (date_str[0] != '\0') ) {
-           num[i] = date_str[0];
-           date_str++;
-           i++;
-       }
-       if ( date_str[0] == ':' ) {
-           date_str++;
-       }
-       num[i] = '\0';
-       gmt.tm_sec = atoi(num);
+
+    if (year < 1970)
+    {
+        SG_LOG(SG_GENERAL, SG_ALERT, "Invalid year '" << year << "'. Use 1970 or later.");
+        return -1;
     }
+
+    parseIntValue(&date_str, &month,  1, 12, "month",  argument);
+    parseIntValue(&date_str, &day,    1, 31, "day",    argument);
+    parseIntValue(&date_str, &hour,   0, 23, "hour",   argument);
+    parseIntValue(&date_str, &minute, 0, 59, "minute", argument);
+    parseIntValue(&date_str, &second, 0, 59, "second", argument);
+
+    gmt.tm_sec  = second;
+    gmt.tm_min  = minute;
+    gmt.tm_hour = hour;
+    gmt.tm_mday = day;
+    gmt.tm_mon  = month - 1;
+    gmt.tm_year = year -1900;
+    gmt.tm_isdst = 0; // ignore daylight savings time for the moment
+
     time_t theTime = sgTimeGetGMT( gmt.tm_year, gmt.tm_mon, gmt.tm_mday,
-                                  gmt.tm_hour, gmt.tm_min, gmt.tm_sec );
-    //printf ("Date is %s\n", ctime(&theTime));
-    //printf ("in seconds that is %d\n", theTime);
-    //exit(1);
+                                   gmt.tm_hour, gmt.tm_min, gmt.tm_sec );
+
+    SG_LOG(SG_GENERAL, SG_INFO, "Configuring startup time to " << ctime(&theTime));
+
     return (theTime);
 }
 
@@ -475,9 +488,9 @@ parse_degree( const string& degree_str) {
 
 
 // parse time offset string into seconds
-static int
+static long int
 parse_time_offset( const string& time_str) {
-    int result;
+   long int result;
 
     // printf("time offset = %s\n", time_str);
 
@@ -493,7 +506,7 @@ parse_time_offset( const string& time_str) {
 }
 
 
-// Parse --fov=x.xx type option 
+// Parse --fov=x.xx type option
 static double
 parse_fov( const string& arg ) {
     double fov = atof(arg);
@@ -520,7 +533,7 @@ parse_fov( const string& arg ) {
 //        point values are ok.
 //
 // Serial example "--nmea=serial,dir,hz,device,baud" where
-// 
+//
 //  device = OS device name of serial line to be open()'ed
 //  baud = {300, 1200, 2400, ..., 230400}
 //
@@ -549,54 +562,6 @@ add_channel( const string& type, const string& channel_str ) {
     return true;
 }
 
-
-// The parse wp and parse flight-plan options don't work anymore, because
-// the route manager and the airport subsystems have not yet been initialized
-// at this stage.
-
-// Parse --wp=ID[@alt]
-static void
-parse_wp( const string& arg ) {
-    string_list *waypoints = globals->get_initial_waypoints();
-    if (!waypoints) {
-        waypoints = new string_list;
-        globals->set_initial_waypoints(waypoints);
-    }
-    waypoints->push_back(arg);
-}
-
-
-// Parse --flight-plan=[file]
-static bool
-parse_flightplan(const string& arg)
-{
-    string_list *waypoints = globals->get_initial_waypoints();
-    if (!waypoints) {
-        waypoints = new string_list;
-        globals->set_initial_waypoints(waypoints);
-    }
-
-    sg_gzifstream in(arg.c_str());
-    if ( !in.is_open() )
-        return false;
-
-    while ( true ) {
-        string line;
-        getline( in, line, '\n' );
-
-        // catch extraneous (DOS) line ending character
-        if ( line[line.length() - 1] < 32 )
-            line = line.substr( 0, line.length()-1 );
-
-        if ( in.eof() )
-            break;
-
-        waypoints->push_back(line);
-    }
-    return true;
-}
-
-
 static int
 fgOptLanguage( const char *arg )
 {
@@ -781,27 +746,13 @@ fgOptRoc( const char *arg )
     return FG_OPTIONS_OK;
 }
 
-static int
-fgOptFgRoot( const char *arg )
-{
-    // this option is dealt with by fgInitFGRoot
-    return FG_OPTIONS_OK;
-}
-
 static int
 fgOptFgScenery( const char *arg )
 {
-    globals->set_fg_scenery(arg);
+    globals->append_fg_scenery(arg);
     return FG_OPTIONS_OK;
 }
 
-static int
-fgOptFgAircraft(const char* arg)
-{
-  // this option is dealt with by fgInitFGAircraft
-  return FG_OPTIONS_OK;
-}
-
 static int
 fgOptFov( const char *arg )
 {
@@ -862,7 +813,7 @@ fgOptBpp( const char *arg )
 static int
 fgOptTimeOffset( const char *arg )
 {
-    fgSetInt("/sim/startup/time-offset",
+    fgSetLong("/sim/startup/time-offset",
                 parse_time_offset( arg ));
     fgSetString("/sim/startup/time-offset-type", "system-offset");
     return FG_OPTIONS_OK;
@@ -871,36 +822,54 @@ fgOptTimeOffset( const char *arg )
 static int
 fgOptStartDateSys( const char *arg )
 {
-    fgSetInt("/sim/startup/time-offset", parse_date( arg ) );
-    fgSetString("/sim/startup/time-offset-type", "system");
+    long int theTime = parse_date( arg, "system" );
+    if (theTime>=0)
+    {
+        fgSetLong("/sim/startup/time-offset",  theTime);
+        fgSetString("/sim/startup/time-offset-type", "system");
+    }
     return FG_OPTIONS_OK;
 }
 
 static int
 fgOptStartDateLat( const char *arg )
 {
-    fgSetInt("/sim/startup/time-offset", parse_date( arg ) );
-    fgSetString("/sim/startup/time-offset-type", "latitude");
+    long int theTime = parse_date( arg, "latitude" );
+    if (theTime>=0)
+    {
+        fgSetLong("/sim/startup/time-offset", theTime);
+        fgSetString("/sim/startup/time-offset-type", "latitude");
+    }
     return FG_OPTIONS_OK;
 }
 
 static int
 fgOptStartDateGmt( const char *arg )
 {
-    fgSetInt("/sim/startup/time-offset", parse_date( arg ) );
-    fgSetString("/sim/startup/time-offset-type", "gmt");
+    long int theTime = parse_date( arg, "gmt" );
+    if (theTime>=0)
+    {
+        fgSetLong("/sim/startup/time-offset", theTime);
+        fgSetString("/sim/startup/time-offset-type", "gmt");
+    }
     return FG_OPTIONS_OK;
 }
 
 static int
 fgSetupProxy( const char *arg )
 {
-    string options = arg;
+    string options = simgear::strutils::strip( arg );
     string host, port, auth;
     string::size_type pos;
 
+    // this is NURLP - NURLP is not an url parser
+    if( simgear::strutils::starts_with( options, "http://" ) )
+        options = options.substr( 7 );
+    if( simgear::strutils::ends_with( options, "/" ) )
+        options = options.substr( 0, options.length() - 1 );
+
     host = port = auth = "";
-    if ((pos = options.find("@")) != string::npos) 
+    if ((pos = options.find("@")) != string::npos)
         auth = options.substr(0, pos++);
     else
         pos = 0;
@@ -931,29 +900,20 @@ fgOptTraceRead( const char *arg )
 static int
 fgOptLogLevel( const char *arg )
 {
-    fgSetString("/sim/logging/classes", "all");
     fgSetString("/sim/logging/priority", arg);
-
-    string priority = arg;
-    logbuf::set_log_classes(SG_ALL);
-    if (priority == "bulk") {
-      logbuf::set_log_priority(SG_BULK);
-    } else if (priority == "debug") {
-      logbuf::set_log_priority(SG_DEBUG);
-    } else if (priority == "info") {
-      logbuf::set_log_priority(SG_INFO);
-    } else if (priority == "warn") {
-      logbuf::set_log_priority(SG_WARN);
-    } else if (priority == "alert") {
-      logbuf::set_log_priority(SG_ALERT);
-    } else {
-      SG_LOG(SG_GENERAL, SG_WARN, "Unknown logging priority " << priority);
-    }
-    SG_LOG(SG_GENERAL, SG_DEBUG, "Logging priority is " << priority);
+    setLoggingPriority(arg);
 
     return FG_OPTIONS_OK;
 }
 
+static int
+fgOptLogClasses( const char *arg )
+{
+    fgSetString("/sim/logging/classes", arg);
+    setLoggingClasses (arg);
+
+    return FG_OPTIONS_OK;
+}
 
 static int
 fgOptTraceWrite( const char *arg )
@@ -981,7 +941,7 @@ fgOptViewOffset( const char *arg )
     } else {
        default_view_offset = atof( woffset.c_str() ) * SGD_DEGREES_TO_RADIANS;
     }
-    /* apparently not used (CLO, 11 Jun 2002) 
+    /* apparently not used (CLO, 11 Jun 2002)
         FGViewer *pilot_view =
            (FGViewer *)globals->get_viewmgr()->get_view( 0 ); */
     // this will work without calls to the viewer...
@@ -994,18 +954,14 @@ fgOptViewOffset( const char *arg )
 static int
 fgOptVisibilityMeters( const char *arg )
 {
-    double visibility = atof( arg );
-    fgDefaultWeatherValue("visibility-m", visibility);
-    fgSetDouble("/environment/visibility-m", visibility);
+    Environment::Presets::VisibilitySingleton::instance()->preset( atof( arg ) );
     return FG_OPTIONS_OK;
 }
 
 static int
 fgOptVisibilityMiles( const char *arg )
 {
-    double visibility = atof( arg ) * 5280.0 * SG_FEET_TO_METER;
-    fgDefaultWeatherValue("visibility-m", visibility);
-    fgSetDouble("/environment/visibility-m", visibility);
+    Environment::Presets::VisibilitySingleton::instance()->preset( atof( arg ) * 5280.0 * SG_FEET_TO_METER );
     return FG_OPTIONS_OK;
 }
 
@@ -1016,7 +972,7 @@ fgOptRandomWind( const char *arg )
     double max_hdg = min_hdg + (20 - sqrt(sg_random() * 400));
     double speed = sg_random() * sg_random() * 40;
     double gust = speed + (10 - sqrt(sg_random() * 100));
-    fgSetupWind(min_hdg, max_hdg, speed, gust);
+    Environment::Presets::WindSingleton::instance()->preset(min_hdg, max_hdg, speed, gust);
     return FG_OPTIONS_OK;
 }
 
@@ -1028,14 +984,14 @@ fgOptWind( const char *arg )
        SG_LOG( SG_GENERAL, SG_ALERT, "bad wind value " << arg );
        return FG_OPTIONS_ERROR;
     }
-    fgSetupWind(min_hdg, max_hdg, speed, gust);
+    Environment::Presets::WindSingleton::instance()->preset(min_hdg, max_hdg, speed, gust);
     return FG_OPTIONS_OK;
 }
 
 static int
 fgOptTurbulence( const char *arg )
 {
-    fgDefaultWeatherValue("turbulence/magnitude-norm", atof(arg));
+    Environment::Presets::TurbulenceSingleton::instance()->preset( atof(arg) );
     return FG_OPTIONS_OK;
 }
 
@@ -1052,23 +1008,19 @@ fgOptCeiling( const char *arg )
         elevation = atof(spec.substr(0, pos).c_str());
         thickness = atof(spec.substr(pos + 1).c_str());
     }
-    fgSetDouble("/environment/clouds/layer[0]/elevation-ft", elevation);
-    fgSetDouble("/environment/clouds/layer[0]/thickness-ft", thickness);
-    fgSetString("/environment/clouds/layer[0]/coverage", "overcast");
+    Environment::Presets::CeilingSingleton::instance()->preset( elevation, thickness );
     return FG_OPTIONS_OK;
 }
 
 static int
 fgOptWp( const char *arg )
 {
-    parse_wp( arg );
-    return FG_OPTIONS_OK;
-}
-
-static int
-fgOptFlightPlan( const char *arg )
-{
-    parse_flightplan ( arg );
+    string_list *waypoints = globals->get_initial_waypoints();
+    if (!waypoints) {
+        waypoints = new string_list;
+        globals->set_initial_waypoints(waypoints);
+    }
+    waypoints->push_back(arg);
     return FG_OPTIONS_OK;
 }
 
@@ -1144,12 +1096,22 @@ fgOptNAV2( const char * arg )
 }
 
 static int
-fgOptADF( const char * arg )
+fgOptADF1( const char * arg )
+{
+    double rot, freq;
+    if (parse_colon(arg, &rot, &freq))
+        fgSetDouble("/instrumentation/adf[0]/rotation-deg", rot);
+    fgSetDouble("/instrumentation/adf[0]/frequencies/selected-khz", freq);
+    return FG_OPTIONS_OK;
+}
+
+static int
+fgOptADF2( const char * arg )
 {
     double rot, freq;
     if (parse_colon(arg, &rot, &freq))
-        fgSetDouble("/instrumentation/adf/rotation-deg", rot);
-    fgSetDouble("/instrumentation/adf/frequencies/selected-khz", freq);
+        fgSetDouble("/instrumentation/adf[1]/rotation-deg", rot);
+    fgSetDouble("/instrumentation/adf[1]/frequencies/selected-khz", freq);
     return FG_OPTIONS_OK;
 }
 
@@ -1166,10 +1128,16 @@ fgOptDME( const char *arg )
         fgSetString("/instrumentation/dme/frequencies/source",
                     "/instrumentation/nav[1]/frequencies/selected-mhz");
     } else {
+        double frequency = atof(arg);
+        if (frequency==0.0)
+        {
+            SG_LOG(SG_INPUT, SG_ALERT, "Invalid DME frequency: '" << arg << "'.");
+            return FG_OPTIONS_ERROR;
+        }
         fgSetInt("/instrumentation/dme/switch-position", 2);
         fgSetString("/instrumentation/dme/frequencies/source",
                     "/instrumentation/dme/frequencies/selected-mhz");
-        fgSetString("/instrumentation/dme/frequencies/selected-mhz", arg);
+        fgSetDouble("/instrumentation/dme/frequencies/selected-mhz", frequency);
     }
     return FG_OPTIONS_OK;
 }
@@ -1201,6 +1169,15 @@ fgOptScenario( const char *arg )
     return FG_OPTIONS_OK;
 }
 
+static int
+fgOptNoScenarios( const char *arg )
+{
+    SGPropertyNode_ptr ai_node = fgGetNode( "/sim/ai", true );
+    ai_node->removeChildren("scenario",false);
+    ai_node->setBoolValue( "enabled", false );
+    return FG_OPTIONS_OK;
+}
+
 static int
 fgOptRunway( const char *arg )
 {
@@ -1242,20 +1219,6 @@ fgOptVersion( const char *arg )
     return FG_OPTIONS_EXIT;
 }
 
-static int
-fgOptFpe(const char* arg)
-{
-    // Actually handled in bootstrap.cxx
-    return FG_OPTIONS_OK;
-}
-
-static int
-fgOptFgviewer(const char* arg)
-{
-    // Actually handled in bootstrap.cxx
-    return FG_OPTIONS_OK;
-}
-
 static int
 fgOptCallSign(const char * arg)
 {
@@ -1265,24 +1228,67 @@ fgOptCallSign(const char * arg)
     callsign[10]=0;
     for (i=0;callsign[i];i++)
     {
-        switch (callsign[i])
-        {
-            case 'A'...'Z':break;
-            case 'a'...'z':break;
-            case '0'...'9':break;
-            case '_':case '-':break;
-            default:
-                // convert any other illegal characters
-                callsign[i]='-';
-                break;
-        }
+        char c = callsign[i];
+        if (c >= 'A' && c <= 'Z') continue;
+        if (c >= 'a' && c <= 'z') continue;
+        if (c >= '0' && c <= '9') continue;
+        if (c == '-' || c == '_') continue;
+        // convert any other illegal characters
+        callsign[i]='-';
     }
     fgSetString("sim/multiplay/callsign", callsign );
     return FG_OPTIONS_OK;
 }
 
 
-static map<string,size_t> fgOptionMap;
+// Set a property for the --prop: option. Syntax: --prop:[<type>:]<name>=<value>
+// <type> can be "double" etc. but also only the first letter "d".
+// Examples:  --prop:alpha=1  --prop:bool:beta=true  --prop:d:gamma=0.123
+static int
+fgOptSetProperty(const char* raw)
+{
+  string arg(raw);
+  string::size_type pos = arg.find('=');
+  if (pos == arg.npos || pos == 0 || pos + 1 == arg.size())
+    return FG_OPTIONS_ERROR;
+  
+  string name = arg.substr(0, pos);
+  string value = arg.substr(pos + 1);
+  string type;
+  pos = name.find(':');
+  
+  if (pos != name.npos && pos != 0 && pos + 1 != name.size()) {
+    type = name.substr(0, pos);
+    name = name.substr(pos + 1);
+  }
+  SGPropertyNode *n = fgGetNode(name.c_str(), true);
+  
+  bool writable = n->getAttribute(SGPropertyNode::WRITE);
+  if (!writable)
+    n->setAttribute(SGPropertyNode::WRITE, true);
+  
+  bool ret = false;
+  if (type.empty())
+    ret = n->setUnspecifiedValue(value.c_str());
+  else if (type == "s" || type == "string")
+    ret = n->setStringValue(value.c_str());
+  else if (type == "d" || type == "double")
+    ret = n->setDoubleValue(strtod(value.c_str(), 0));
+  else if (type == "f" || type == "float")
+    ret = n->setFloatValue(atof(value.c_str()));
+  else if (type == "l" || type == "long")
+    ret =  n->setLongValue(strtol(value.c_str(), 0, 0));
+  else if (type == "i" || type == "int")
+    ret =  n->setIntValue(atoi(value.c_str()));
+  else if (type == "b" || type == "bool")
+    ret =  n->setBoolValue(value == "true" || atoi(value.c_str()) != 0);
+  
+  if (!writable)
+    n->setAttribute(SGPropertyNode::WRITE, false);
+  return ret ? FG_OPTIONS_OK : FG_OPTIONS_ERROR;
+}
+
+
 
 /*
    option       has_param type        property         b_param s_param  func
@@ -1311,18 +1317,22 @@ where:
     argument.
 */
 
-enum OptionType { OPTION_BOOL, OPTION_STRING, OPTION_DOUBLE, OPTION_INT, OPTION_CHANNEL, OPTION_FUNC };
+enum OptionType { OPTION_BOOL = 0, OPTION_STRING, OPTION_DOUBLE, OPTION_INT, OPTION_CHANNEL, OPTION_FUNC, OPTION_IGNORE };
+const int OPTION_MULTI = 1 << 17;
+
 struct OptionDesc {
     const char *option;
     bool has_param;
-    enum OptionType type;
+    int type;
     const char *property;
     bool b_param;
     const char *s_param;
     int (*func)( const char * );
     } fgOptionArray[] = {
-       
+
     {"language",                     true,  OPTION_FUNC,   "", false, "", fgOptLanguage },
+    {"disable-rembrandt",            false, OPTION_BOOL,   "/sim/rendering/rembrandt", false, "", 0 },
+    {"enable-rembrandt",             false, OPTION_BOOL,   "/sim/rendering/rembrandt", true, "", 0 },
     {"disable-game-mode",            false, OPTION_BOOL,   "/sim/startup/game-mode", false, "", 0 },
     {"enable-game-mode",             false, OPTION_BOOL,   "/sim/startup/game-mode", true, "", 0 },
     {"disable-splash-screen",        false, OPTION_BOOL,   "/sim/startup/splash-screen", false, "", 0 },
@@ -1338,22 +1348,24 @@ struct OptionDesc {
     {"metar",                        true,  OPTION_STRING, "/environment/metar/data", false, "", 0 },
     {"disable-ai-models",            false, OPTION_BOOL,   "/sim/ai/enabled", false, "", 0 },
     {"enable-ai-models",             false, OPTION_BOOL,   "/sim/ai/enabled", true, "", 0 },
+    {"disable-ai-traffic",           false, OPTION_BOOL,   "/sim/traffic-manager/enabled", false, "", 0 },
+    {"enable-ai-traffic",            false, OPTION_BOOL,   "/sim/traffic-manager/enabled", true,  "", 0 },
     {"disable-freeze",               false, OPTION_BOOL,   "/sim/freeze/master", false, "", 0 },
     {"enable-freeze",                false, OPTION_BOOL,   "/sim/freeze/master", true, "", 0 },
     {"disable-fuel-freeze",          false, OPTION_BOOL,   "/sim/freeze/fuel", false, "", 0 },
     {"enable-fuel-freeze",           false, OPTION_BOOL,   "/sim/freeze/fuel", true, "", 0 },
     {"disable-clock-freeze",         false, OPTION_BOOL,   "/sim/freeze/clock", false, "", 0 },
     {"enable-clock-freeze",          false, OPTION_BOOL,   "/sim/freeze/clock", true, "", 0 },
-    {"disable-hud-3d",               false, OPTION_BOOL,   "/sim/hud/enable3d", false, "", 0 },
-    {"enable-hud-3d",                false, OPTION_BOOL,   "/sim/hud/enable3d", true, "", 0 },
+    {"disable-hud-3d",               false, OPTION_BOOL,   "/sim/hud/enable3d[1]", false, "", 0 },
+    {"enable-hud-3d",                false, OPTION_BOOL,   "/sim/hud/enable3d[1]", true, "", 0 },
     {"disable-anti-alias-hud",       false, OPTION_BOOL,   "/sim/hud/color/antialiased", false, "", 0 },
     {"enable-anti-alias-hud",        false, OPTION_BOOL,   "/sim/hud/color/antialiased", true, "", 0 },
     {"control",                      true,  OPTION_STRING, "/sim/control-mode", false, "", 0 },
-    {"disable-auto-coordination",    false, OPTION_BOOL,   "/sim/auto-coordination", false, "", 0 },
-    {"enable-auto-coordination",     false, OPTION_BOOL,   "/sim/auto-coordination", true, "", 0 },
+    {"disable-auto-coordination",    false, OPTION_BOOL,   "/controls/flight/auto-coordination", false, "", 0 },
+    {"enable-auto-coordination",     false, OPTION_BOOL,   "/controls/flight/auto-coordination", true, "", 0 },
     {"browser-app",                  true,  OPTION_STRING, "/sim/startup/browser-app", false, "", 0 },
-    {"disable-hud",                  false, OPTION_BOOL,   "/sim/hud/visibility", false, "", 0 },
-    {"enable-hud",                   false, OPTION_BOOL,   "/sim/hud/visibility", true, "", 0 },
+    {"disable-hud",                  false, OPTION_BOOL,   "/sim/hud/visibility[1]", false, "", 0 },
+    {"enable-hud",                   false, OPTION_BOOL,   "/sim/hud/visibility[1]", true, "", 0 },
     {"disable-panel",                false, OPTION_BOOL,   "/sim/panel/visibility", false, "", 0 },
     {"enable-panel",                 false, OPTION_BOOL,   "/sim/panel/visibility", true, "", 0 },
     {"disable-sound",                false, OPTION_BOOL,   "/sim/sound/working", false, "", 0 },
@@ -1362,7 +1374,9 @@ struct OptionDesc {
     {"airport",                      true,  OPTION_STRING, "/sim/presets/airport-id", false, "", 0 },
     {"runway",                       true,  OPTION_FUNC,   "", false, "", fgOptRunway },
     {"vor",                          true,  OPTION_FUNC,   "", false, "", fgOptVOR },
+    {"vor-frequency",                true,  OPTION_DOUBLE, "/sim/presets/vor-freq", false, "", fgOptVOR },
     {"ndb",                          true,  OPTION_FUNC,   "", false, "", fgOptNDB },
+    {"ndb-frequency",                true,  OPTION_DOUBLE, "/sim/presets/ndb-freq", false, "", fgOptVOR },
     {"carrier",                      true,  OPTION_FUNC,   "", false, "", fgOptCarrier },
     {"parkpos",                      true,  OPTION_FUNC,   "", false, "", fgOptParkpos },
     {"fix",                          true,  OPTION_FUNC,   "", false, "", fgOptFIX },
@@ -1384,12 +1398,12 @@ struct OptionDesc {
     {"pitch",                        true,  OPTION_DOUBLE, "/sim/presets/pitch-deg", false, "", 0 },
     {"glideslope",                   true,  OPTION_DOUBLE, "/sim/presets/glideslope-deg", false, "", 0 },
     {"roc",                          true,  OPTION_FUNC,   "", false, "", fgOptRoc },
-    {"fg-root",                      true,  OPTION_FUNC,   "", false, "", fgOptFgRoot },
-    {"fg-scenery",                   true,  OPTION_FUNC,   "", false, "", fgOptFgScenery },
-    {"fg-aircraft",                  true,  OPTION_FUNC,   "", false, "", fgOptFgAircraft },
+    {"fg-root",                      true,  OPTION_IGNORE,   "", false, "", 0 },
+    {"fg-scenery",                   true,  OPTION_FUNC | OPTION_MULTI,   "", false, "", fgOptFgScenery },
+    {"fg-aircraft",                  true,  OPTION_IGNORE | OPTION_MULTI,   "", false, "", 0 },
     {"fdm",                          true,  OPTION_STRING, "/sim/flight-model", false, "", 0 },
     {"aero",                         true,  OPTION_STRING, "/sim/aero", false, "", 0 },
-    {"aircraft-dir",                 true,  OPTION_STRING, "/sim/aircraft-dir", false, "", 0 },
+    {"aircraft-dir",                 true,  OPTION_IGNORE,   "", false, "", 0 },
     {"model-hz",                     true,  OPTION_INT,    "/sim/model-hz", false, "", 0 },
     {"speed",                        true,  OPTION_INT,    "/sim/speed-up", false, "", 0 },
     {"trim",                         false, OPTION_BOOL,   "/sim/presets/trim", true, "", 0 },
@@ -1423,9 +1437,13 @@ struct OptionDesc {
     {"enable-skyblend",              false, OPTION_BOOL,   "/sim/rendering/skyblend", true, "", 0 },
     {"disable-textures",             false, OPTION_BOOL,   "/sim/rendering/textures", false, "", 0 },
     {"enable-textures",              false, OPTION_BOOL,   "/sim/rendering/textures", true, "", 0 },
-    {"texture-filtering",           false, OPTION_INT,    "/sim/rendering/filtering", 1, "", 0 },
+    {"texture-filtering",            false, OPTION_INT,    "/sim/rendering/filtering", 1, "", 0 },
     {"disable-wireframe",            false, OPTION_BOOL,   "/sim/rendering/wireframe", false, "", 0 },
     {"enable-wireframe",             false, OPTION_BOOL,   "/sim/rendering/wireframe", true, "", 0 },
+    {"materials-file",               true,  OPTION_STRING, "/sim/rendering/materials-file", false, "", 0 },
+    {"disable-terrasync",            false, OPTION_BOOL,   "/sim/terrasync/enabled", false, "", 0 },
+    {"enable-terrasync",             false, OPTION_BOOL,   "/sim/terrasync/enabled", true, "", 0 },
+    {"terrasync-dir",                true,  OPTION_STRING, "/sim/terrasync/scenery-dir", false, "", 0 },
     {"geometry",                     true,  OPTION_FUNC,   "", false, "", fgOptGeometry },
     {"bpp",                          true,  OPTION_FUNC,   "", false, "", fgOptBpp },
     {"units-feet",                   false, OPTION_STRING, "/sim/startup/units", false, "feet", 0 },
@@ -1441,23 +1459,25 @@ struct OptionDesc {
     {"hud-tris",                     false, OPTION_STRING, "/sim/hud/frame-stat-type", false, "tris", 0 },
     {"hud-culled",                   false, OPTION_STRING, "/sim/hud/frame-stat-type", false, "culled", 0 },
     {"atcsim",                       true,  OPTION_CHANNEL, "", false, "dummy", 0 },
-    {"atlas",                        true,  OPTION_CHANNEL, "", false, "", 0 },
+    {"atlas",                        true,  OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
     {"httpd",                        true,  OPTION_CHANNEL, "", false, "", 0 },
 #ifdef FG_JPEG_SERVER
     {"jpg-httpd",                    true,  OPTION_CHANNEL, "", false, "", 0 },
 #endif
-    {"native",                       true,  OPTION_CHANNEL, "", false, "", 0 },
-    {"native-ctrls",                 true,  OPTION_CHANNEL, "", false, "", 0 },
-    {"native-fdm",                   true,  OPTION_CHANNEL, "", false, "", 0 },
-    {"native-gui",                   true,  OPTION_CHANNEL, "", false, "", 0 },
-    {"opengc",                       true,  OPTION_CHANNEL, "", false, "", 0 },
-    {"AV400",                        true,  OPTION_CHANNEL, "", false, "", 0 },
-    {"AV400Sim",                     true,  OPTION_CHANNEL, "", false, "", 0 },
-    {"garmin",                       true,  OPTION_CHANNEL, "", false, "", 0 },
-    {"nmea",                         true,  OPTION_CHANNEL, "", false, "", 0 },
-    {"generic",                      true,  OPTION_CHANNEL, "", false, "", 0 },
-    {"props",                        true,  OPTION_CHANNEL, "", false, "", 0 },
-    {"telnet",                       true,  OPTION_CHANNEL, "", false, "", 0 },
+    {"native",                       true,  OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+    {"native-ctrls",                 true,  OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+    {"native-fdm",                   true,  OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+    {"native-gui",                   true,  OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+    {"opengc",                       true,  OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+    {"AV400",                        true,  OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+    {"AV400Sim",                     true,  OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+    {"AV400WSimA",                   true,  OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+    {"AV400WSimB",                   true,  OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+    {"garmin",                       true,  OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+    {"nmea",                         true,  OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+    {"generic",                      true,  OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+    {"props",                        true,  OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+    {"telnet",                       true,  OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
     {"pve",                          true,  OPTION_CHANNEL, "", false, "", 0 },
     {"ray",                          true,  OPTION_CHANNEL, "", false, "", 0 },
     {"rul",                          true,  OPTION_CHANNEL, "", false, "", 0 },
@@ -1465,437 +1485,795 @@ struct OptionDesc {
     {"jsclient",                     true,  OPTION_CHANNEL, "", false, "", 0 },
     {"proxy",                        true,  OPTION_FUNC,    "", false, "", fgSetupProxy },
     {"callsign",                     true,  OPTION_FUNC,    "", false, "", fgOptCallSign},
-    {"multiplay",                    true,  OPTION_CHANNEL, "", false, "", 0 },
-    {"trace-read",                   true,  OPTION_FUNC,   "", false, "", fgOptTraceRead },
-    {"trace-write",                  true,  OPTION_FUNC,   "", false, "", fgOptTraceWrite },
+    {"multiplay",                    true,  OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+#ifdef FG_HAVE_HLA
+    {"hla",                          true,  OPTION_CHANNEL, "", false, "", 0 },
+#endif
+    {"trace-read",                   true,  OPTION_FUNC | OPTION_MULTI,   "", false, "", fgOptTraceRead },
+    {"trace-write",                  true,  OPTION_FUNC | OPTION_MULTI,   "", false, "", fgOptTraceWrite },
     {"log-level",                    true,  OPTION_FUNC,   "", false, "", fgOptLogLevel },
-    {"view-offset",                  true,  OPTION_FUNC,   "", false, "", fgOptViewOffset },
+    {"log-class",                    true,  OPTION_FUNC,   "", false, "", fgOptLogClasses },
+    {"view-offset",                  true,  OPTION_FUNC | OPTION_MULTI,   "", false, "", fgOptViewOffset },
     {"visibility",                   true,  OPTION_FUNC,   "", false, "", fgOptVisibilityMeters },
     {"visibility-miles",             true,  OPTION_FUNC,   "", false, "", fgOptVisibilityMiles },
     {"random-wind",                  false, OPTION_FUNC,   "", false, "", fgOptRandomWind },
-    {"wind",                         true,  OPTION_FUNC,   "", false, "", fgOptWind },
+    {"wind",                         true,  OPTION_FUNC | OPTION_MULTI,   "", false, "", fgOptWind },
     {"turbulence",                   true,  OPTION_FUNC,   "", false, "", fgOptTurbulence },
     {"ceiling",                      true,  OPTION_FUNC,   "", false, "", fgOptCeiling },
-    {"wp",                           true,  OPTION_FUNC,   "", false, "", fgOptWp },
-    {"flight-plan",                  true,  OPTION_FUNC,   "", false, "", fgOptFlightPlan },
-    {"config",                       true,  OPTION_FUNC,   "", false, "", fgOptConfig },
+    {"wp",                           true,  OPTION_FUNC | OPTION_MULTI,   "", false, "", fgOptWp },
+    {"flight-plan",                  true,  OPTION_STRING,   "/autopilot/route-manager/file-path", false, "", NULL },
+    {"config",                       true,  OPTION_FUNC | OPTION_MULTI,   "", false, "", fgOptConfig },
     {"aircraft",                     true,  OPTION_STRING, "/sim/aircraft", false, "", 0 },
     {"vehicle",                      true,  OPTION_STRING, "/sim/aircraft", false, "", 0 },
-    {"failure",                      true,  OPTION_FUNC,   "", false, "", fgOptFailure },
+    {"failure",                      true,  OPTION_FUNC | OPTION_MULTI,   "", false, "", fgOptFailure },
     {"com1",                         true,  OPTION_DOUBLE, "/instrumentation/comm[0]/frequencies/selected-mhz", false, "", 0 },
     {"com2",                         true,  OPTION_DOUBLE, "/instrumentation/comm[1]/frequencies/selected-mhz", false, "", 0 },
     {"nav1",                         true,  OPTION_FUNC,   "", false, "", fgOptNAV1 },
     {"nav2",                         true,  OPTION_FUNC,   "", false, "", fgOptNAV2 },
-    {"adf",                          true,  OPTION_FUNC,   "", false, "", fgOptADF },
+    {"adf", /*legacy*/               true,  OPTION_FUNC,   "", false, "", fgOptADF1 },
+    {"adf1",                         true,  OPTION_FUNC,   "", false, "", fgOptADF1 },
+    {"adf2",                         true,  OPTION_FUNC,   "", false, "", fgOptADF2 },
     {"dme",                          true,  OPTION_FUNC,   "", false, "", fgOptDME },
     {"min-status",                   true,  OPTION_STRING,  "/sim/aircraft-min-status", false, "all", 0 },
     {"livery",                       true,  OPTION_FUNC,   "", false, "", fgOptLivery },
-    {"ai-scenario",                  true,  OPTION_FUNC,   "", false, "", fgOptScenario },
+    {"ai-scenario",                  true,  OPTION_FUNC | OPTION_MULTI,   "", false, "", fgOptScenario },
+    {"disable-ai-scenarios",         false, OPTION_FUNC,   "", false, "", fgOptNoScenarios},
     {"parking-id",                   true,  OPTION_FUNC,   "", false, "", fgOptParking  },
     {"version",                      false, OPTION_FUNC,   "", false, "", fgOptVersion },
-    {"enable-fpe",                  false, OPTION_FUNC,   "", false, "", fgOptFpe},
-    {"fgviewer",                    false, OPTION_FUNC,   "", false, "", fgOptFgviewer},
+    {"enable-fpe",                   false, OPTION_IGNORE,   "", false, "", 0},
+    {"fgviewer",                     false, OPTION_IGNORE,   "", false, "", 0},
+    {"prop",                         true,  OPTION_FUNC | OPTION_MULTI,   "", false, "", fgOptSetProperty},
     {0}
 };
 
 
-// Set a property for the --prop: option. Syntax: --prop:[<type>:]<name>=<value>
-// <type> can be "double" etc. but also only the first letter "d".
-// Examples:  --prop:alpha=1  --prop:bool:beta=true  --prop:d:gamma=0.123
-static bool
-set_property(const string& arg)
+namespace flightgear
 {
-    string::size_type pos = arg.find('=');
-    if (pos == arg.npos || pos == 0 || pos + 1 == arg.size())
-        return false;
 
-    string name = arg.substr(0, pos);
-    string value = arg.substr(pos + 1);
-    string type;
-    pos = name.find(':');
+/**
+ * internal storage of a value->option binding
+ */
+class OptionValue 
+{
+public:
+  OptionValue(OptionDesc* d, const string& v) :
+    desc(d), value(v)
+  {;}
+  
+  OptionDesc* desc;
+  string value;
+};
 
-    if (pos != name.npos && pos != 0 && pos + 1 != name.size()) {
-        type = name.substr(0, pos);
-        name = name.substr(pos + 1);
+typedef std::vector<OptionValue> OptionValueVec;
+typedef std::map<string, OptionDesc*> OptionDescDict;
+  
+class Options::OptionsPrivate
+{
+public:
+  
+  OptionValueVec::const_iterator findValue(const string& key) const
+  {
+    OptionValueVec::const_iterator it = values.begin();
+    for (; it != values.end(); ++it) {
+      if (!it->desc) {
+        continue; // ignore markers
+      }
+      
+      if (it->desc->option == key) {
+        return it;
+      }
+    } // of set values iteration
+    
+    return it; // not found
+  }
+  
+  OptionDesc* findOption(const string& key) const
+  {
+    OptionDescDict::const_iterator it = options.find(key);
+    if (it == options.end()) {
+      return NULL;
     }
-    SGPropertyNode *n = fgGetNode(name.c_str(), true);
-
-    bool writable = n->getAttribute(SGPropertyNode::WRITE);
-    if (!writable)
-        n->setAttribute(SGPropertyNode::WRITE, true);
-
-    bool ret = false;
-    if (type.empty())
-        ret = n->setUnspecifiedValue(value.c_str());
-    else if (type == "s" || type == "string")
-        ret = n->setStringValue(value.c_str());
-    else if (type == "d" || type == "double")
-        ret = n->setDoubleValue(strtod(value.c_str(), 0));
-    else if (type == "f" || type == "float")
-        ret = n->setFloatValue(atof(value.c_str()));
-    else if (type == "l" || type == "long")
-        ret =  n->setLongValue(strtol(value.c_str(), 0, 0));
-    else if (type == "i" || type == "int")
-        ret =  n->setIntValue(atoi(value.c_str()));
-    else if (type == "b" || type == "bool")
-        ret =  n->setBoolValue(value == "true" || atoi(value.c_str()) != 0);
-
-    if (!writable)
-        n->setAttribute(SGPropertyNode::WRITE, false);
-    return ret;
-}
-
-
-// Parse a single option
-static int
-parse_option (const string& arg)
-{
-    if ( fgOptionMap.size() == 0 ) {
-        size_t i = 0;
-        OptionDesc *pt = &fgOptionArray[ 0 ];
-        while ( pt->option != 0 ) {
-            fgOptionMap[ pt->option ] = i;
-            i += 1;
-            pt += 1;
-        }
+    
+    return it->second;
+  }
+  
+  int processOption(OptionDesc* desc, const string& arg_value)
+  {
+    if (!desc) {
+      return FG_OPTIONS_OK; // tolerate marker options
     }
-
-    // General Options
-    if ( (arg == "--help") || (arg == "-h") ) {
-       // help/usage request
-       return(FG_OPTIONS_HELP);
-    } else if ( (arg == "--verbose") || (arg == "-v") ) {
-        // verbose help/usage request
-        return(FG_OPTIONS_VERBOSE_HELP);
-    } else if ( arg.find( "--show-aircraft") == 0) {
-        return(FG_OPTIONS_SHOW_AIRCRAFT);
-    } else if ( arg.find( "--show-sound-devices") == 0) {
-        return(FG_OPTIONS_SHOW_SOUND_DEVICES);
-    } else if ( arg.find( "--prop:" ) == 0 ) {
-        if (!set_property(arg.substr(7))) {
-            SG_LOG( SG_GENERAL, SG_ALERT, "Bad property assignment: " << arg );
-            return FG_OPTIONS_ERROR;
+    
+    switch ( desc->type & 0xffff ) {
+      case OPTION_BOOL:
+        fgSetBool( desc->property, desc->b_param );
+        break;
+      case OPTION_STRING:
+        if ( desc->has_param && !arg_value.empty() ) {
+          fgSetString( desc->property, arg_value.c_str() );
+        } else if ( !desc->has_param && arg_value.empty() ) {
+          fgSetString( desc->property, desc->s_param );
+        } else if ( desc->has_param ) {
+          SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
+          return FG_OPTIONS_ERROR;
+        } else {
+          SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' does not have a parameter" );
+          return FG_OPTIONS_ERROR;
         }
-    } else if ( arg.find("-psn_") == 0) {
-    // on Mac, when launched from the GUI, we are passed the ProcessSerialNumber
-    // as an argument (and no others). Silently ignore the argument here.
-        return FG_OPTIONS_OK;
-    } else if ( arg.find( "--" ) == 0 ) {
-        size_t pos = arg.find( '=' );
-        string arg_name, arg_value;
-        if ( pos == string::npos ) {
-            arg_name = arg.substr( 2 );
+        break;
+      case OPTION_DOUBLE:
+        if ( !arg_value.empty() ) {
+          fgSetDouble( desc->property, atof( arg_value ) );
         } else {
-            arg_name = arg.substr( 2, pos - 2 );
-            arg_value = arg.substr( pos + 1);
+          SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
+          return FG_OPTIONS_ERROR;
         }
-        map<string,size_t>::iterator it = fgOptionMap.find( arg_name );
-        if ( it != fgOptionMap.end() ) {
-            OptionDesc *pt = &fgOptionArray[ it->second ];
-            switch ( pt->type ) {
-                case OPTION_BOOL:
-                    fgSetBool( pt->property, pt->b_param );
-                    break;
-                case OPTION_STRING:
-                    if ( pt->has_param && !arg_value.empty() ) {
-                        fgSetString( pt->property, arg_value.c_str() );
-                    } else if ( !pt->has_param && arg_value.empty() ) {
-                        fgSetString( pt->property, pt->s_param );
-                    } else if ( pt->has_param ) {
-                        SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
-                        return FG_OPTIONS_ERROR;
-                    } else {
-                        SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' does not have a parameter" );
-                        return FG_OPTIONS_ERROR;
-                    }
-                    break;
-                case OPTION_DOUBLE:
-                    if ( !arg_value.empty() ) {
-                        fgSetDouble( pt->property, atof( arg_value ) );
-                    } else {
-                        SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
-                        return FG_OPTIONS_ERROR;
-                    }
-                    break;
-                case OPTION_INT:
-                    if ( !arg_value.empty() ) {
-                        fgSetInt( pt->property, atoi( arg_value ) );
-                    } else {
-                        SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
-                        return FG_OPTIONS_ERROR;
-                    }
-                    break;
-                case OPTION_CHANNEL:
-                    // XXX return value of add_channel should be checked?
-                    if ( pt->has_param && !arg_value.empty() ) {
-                        add_channel( pt->option, arg_value );
-                    } else if ( !pt->has_param && arg_value.empty() ) {
-                        add_channel( pt->option, pt->s_param );
-                    } else if ( pt->has_param ) {
-                        SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
-                        return FG_OPTIONS_ERROR;
-                    } else {
-                        SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' does not have a parameter" );
-                        return FG_OPTIONS_ERROR;
-                    }
-                    break;
-                case OPTION_FUNC:
-                    if ( pt->has_param && !arg_value.empty() ) {
-                        return pt->func( arg_value.c_str() );
-                    } else if ( !pt->has_param && arg_value.empty() ) {
-                        return pt->func( pt->s_param );
-                    } else if ( pt->has_param ) {
-                        SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
-                        return FG_OPTIONS_ERROR;
-                    } else {
-                        SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' does not have a parameter" );
-                        return FG_OPTIONS_ERROR;
-                    }
-                    break;
-            }
+        break;
+      case OPTION_INT:
+        if ( !arg_value.empty() ) {
+          fgSetInt( desc->property, atoi( arg_value ) );
         } else {
-            SG_LOG( SG_GENERAL, SG_ALERT, "Unknown option '" << arg << "'" );
-            return FG_OPTIONS_ERROR;
+          SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
+          return FG_OPTIONS_ERROR;
         }
-    } else {
-        SG_LOG( SG_GENERAL, SG_ALERT, "Unknown option '" << arg << "'" );
-        return FG_OPTIONS_ERROR;
+        break;
+      case OPTION_CHANNEL:
+        // XXX return value of add_channel should be checked?
+        if ( desc->has_param && !arg_value.empty() ) {
+          add_channel( desc->option, arg_value );
+        } else if ( !desc->has_param && arg_value.empty() ) {
+          add_channel( desc->option, desc->s_param );
+        } else if ( desc->has_param ) {
+          SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
+          return FG_OPTIONS_ERROR;
+        } else {
+          SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' does not have a parameter" );
+          return FG_OPTIONS_ERROR;
+        }
+        break;
+      case OPTION_FUNC:
+        if ( desc->has_param && !arg_value.empty() ) {
+          return desc->func( arg_value.c_str() );
+        } else if ( !desc->has_param && arg_value.empty() ) {
+          return desc->func( desc->s_param );
+        } else if ( desc->has_param ) {
+          SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
+          return FG_OPTIONS_ERROR;
+        } else {
+          SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' does not have a parameter" );
+          return FG_OPTIONS_ERROR;
+        }
+        break;
+        
+      case OPTION_IGNORE:
+        break;
     }
-
+    
     return FG_OPTIONS_OK;
+  }
+    
+  /**
+   * insert a marker value into the values vector. This is necessary
+   * when processing options, to ensure the correct ordering, where we scan
+   * for marker values in reverse, and then forwards within each group.
+   */
+  void insertGroupMarker()
+  {
+    values.push_back(OptionValue(NULL, "-"));
+  }
+  
+  /**
+   * given a current iterator into the values, find the preceeding group marker,
+   * or return the beginning of the value vector.
+   */
+  OptionValueVec::const_iterator rfindGroup(OptionValueVec::const_iterator pos) const
+  {
+    while (--pos != values.begin()) {
+      if (pos->desc == NULL) {
+        return pos; // found a marker, we're done
+      }
+    }
+    
+    return pos;
+  }
+  
+  bool showHelp,
+    verbose,
+    showAircraft;
+
+  OptionDescDict options;
+  OptionValueVec values;
+  simgear::PathList propertyFiles;
+};
+  
+Options* Options::sharedInstance()
+{
+  if (shared_instance == NULL) {
+    shared_instance = new Options;
+  }
+  
+  return shared_instance;
+}
+  
+Options::Options() :
+  p(new OptionsPrivate())
+{
+  p->showHelp = false;
+  p->verbose = false;
+  p->showAircraft = false;
+  
+// build option map
+  OptionDesc *desc = &fgOptionArray[ 0 ];
+  while ( desc->option != 0 ) {
+    p->options[ desc->option ] = desc;
+    ++desc;
+  }
 }
+  
+Options::~Options()
+{
+}
+  
+void Options::init(int argc, char **argv, const SGPath& appDataPath)
+{
+  fgSetDefaults();
+  
+// first, process the command line
+  bool inOptions = true;
+  for (int i=1; i<argc; ++i) {
+    if (inOptions && (argv[i][0] == '-')) {
+      if (strcmp(argv[i], "--") == 0) { // end of options delimiter
+        inOptions = true;
+        continue;
+      }
+      
+      int result = parseOption(argv[i]);
+      processArgResult(result);
+    } else {
+    // XML properties file
+      SGPath f(argv[i]);
+      if (!f.exists()) {
+        SG_LOG(SG_GENERAL, SG_ALERT, "config file not found:" << f.str());
+        return;
+      }
+      
+      p->propertyFiles.push_back(f);
+    }
+  } // of arguments iteration
+  p->insertGroupMarker(); // command line is one group
+  
+// then config files
+  SGPath config;
+  
+  if( homedir && hostname && strlen(hostname) > 0 ) {
+    // Check for ~/.fgfsrc.hostname
+    config.set(homedir);
+    config.append(".fgfsrc");
+    config.concat( "." );
+    config.concat( hostname );
+    readConfig(config);
+  }
+  
+// Check for ~/.fgfsrc
+  if( homedir ) {
+    config.set(homedir);
+    config.append(".fgfsrc");
+    readConfig(config);
+  }
+  
+// check for a config file in app data
+  SGPath appDataConfig(appDataPath);
+  appDataConfig.append("fgfsrc");
+  if (appDataConfig.exists()) {
+    readConfig(appDataConfig);
+  }
+  
+// setup FG_ROOT
+  setupRoot();
+  
+// system.fgfsrc handling
+  if( hostname && strlen(hostname) > 0 ) {
+    config.set(globals->get_fg_root());
+    config.append( "system.fgfsrc" );
+    config.concat( "." );
+    config.concat( hostname );
+    readConfig(config);
+  }
 
-
-// Parse the command line options
-void
-fgParseArgs (int argc, char **argv)
+  config.set(globals->get_fg_root());
+  config.append( "system.fgfsrc" );
+  readConfig(config);
+}
+  
+void Options::initAircraft()
 {
-    bool in_options = true;
-    bool verbose = false;
-    bool help = false;
-
-    SG_LOG(SG_GENERAL, SG_ALERT, "Processing command line arguments");
-
-    for (int i = 1; i < argc; i++) {
-        string arg = argv[i];
-
-       if (in_options && (arg.find('-') == 0)) {
-         if (arg == "--") {
-           in_options = false;
-         } else {
-           int result = parse_option(arg);
-           if ((result == FG_OPTIONS_HELP) || (result == FG_OPTIONS_ERROR))
-              help = true;
-
-            else if (result == FG_OPTIONS_VERBOSE_HELP)
-              verbose = true;
-
-            else if (result == FG_OPTIONS_SHOW_AIRCRAFT) {
-              fgOptLogLevel( "alert" );
-              SGPath path( globals->get_fg_root() );
-              path.append("Aircraft");
-              fgShowAircraft(path);
-              exit(0);
-
-            } else if (result == FG_OPTIONS_SHOW_SOUND_DEVICES) {
-              SGSoundMgr smgr;
-
-              smgr.init();
-              string vendor = smgr.get_vendor();
-              string renderer = smgr.get_renderer();
-              cout << renderer << " provided by " << vendor << endl;
-              cout << endl << "No. Device" << endl;
-
-              vector <const char*>devices = smgr.get_available_devices();
-              for (vector <const char*>::size_type i=0; i<devices.size(); i++) {
-                cout << i << ".  \"" << devices[i] << "\"" << endl;
-              }
-              devices.clear();
-              exit(0);
-            }
+  BOOST_FOREACH(const string& paths, valuesForOption("fg-aircraft")) {
+    globals->append_aircraft_paths(paths);
+  }
+  
+  const char* envp = ::getenv("FG_AIRCRAFT");
+  if (envp) {
+    globals->append_aircraft_paths(envp);
+  }
 
-            else if (result == FG_OPTIONS_EXIT)
-               exit(0);
-         }
-       } else {
-         in_options = false;
-         SG_LOG(SG_GENERAL, SG_INFO,
-                "Reading command-line property file " << arg);
-         readProperties(arg, globals->get_props());
-       }
+  string aircraft;
+  if (isOptionSet("aircraft")) {
+    aircraft = valueForOption("aircraft");
+  } else if (isOptionSet("vehicle")) {
+    aircraft = valueForOption("vehicle");
+  }
+    
+  if (!aircraft.empty()) {
+    SG_LOG(SG_INPUT, SG_INFO, "aircraft = " << aircraft );
+    fgSetString("/sim/aircraft", aircraft.c_str() );
+  } else {
+    SG_LOG(SG_INPUT, SG_INFO, "No user specified aircraft, using default" );
+  }
+  
+  if (p->showAircraft) {
+    fgOptLogLevel( "alert" );
+    SGPath path( globals->get_fg_root() );
+    path.append("Aircraft");
+    fgShowAircraft(path);
+    exit(0);
+  }
+  
+  if (isOptionSet("aircraft-dir")) {
+    // set this now, so it's available in FindAndCacheAircraft
+    fgSetString("/sim/aircraft-dir", valueForOption("aircraft-dir"));
+  }
+}
+  
+void Options::processArgResult(int result)
+{
+  if ((result == FG_OPTIONS_HELP) || (result == FG_OPTIONS_ERROR))
+    p->showHelp = true;
+  else if (result == FG_OPTIONS_VERBOSE_HELP)
+    p->verbose = true;
+  else if (result == FG_OPTIONS_SHOW_AIRCRAFT) {
+    p->showAircraft = true;    
+  } else if (result == FG_OPTIONS_SHOW_SOUND_DEVICES) {
+    SGSoundMgr smgr;
+    
+    smgr.init();
+    string vendor = smgr.get_vendor();
+    string renderer = smgr.get_renderer();
+    cout << renderer << " provided by " << vendor << endl;
+    cout << endl << "No. Device" << endl;
+    
+    vector <const char*>devices = smgr.get_available_devices();
+    for (vector <const char*>::size_type i=0; i<devices.size(); i++) {
+      cout << i << ".  \"" << devices[i] << "\"" << endl;
     }
-
-    if (help) {
-       fgOptLogLevel( "alert" );
-       fgUsage(verbose);
-       exit(0);
+    devices.clear();
+    exit(0);
+  } else if (result == FG_OPTIONS_EXIT) {
+    exit(0);
+  }
+}
+  
+void Options::readConfig(const SGPath& path)
+{
+  sg_gzifstream in( path.str() );
+  if ( !in.is_open() ) {
+    return;
+  }
+  
+  SG_LOG( SG_GENERAL, SG_INFO, "Processing config file: " << path.str() );
+  
+  in >> skipcomment;
+  while ( ! in.eof() ) {
+    string line;
+    getline( in, line, '\n' );
+    
+    // catch extraneous (DOS) line ending character
+    int i;
+    for (i = line.length(); i > 0; i--)
+      if (line[i - 1] > 32)
+        break;
+    line = line.substr( 0, i );
+    
+    if ( parseOption( line ) == FG_OPTIONS_ERROR ) {
+      cerr << endl << "Config file parse error: " << path.str() << " '"
+      << line << "'" << endl;
+           p->showHelp = true;
     }
+    in >> skipcomment;
+  }
 
-    SG_LOG(SG_GENERAL, SG_INFO, "Finished command line arguments");
+  p->insertGroupMarker(); // each config file is a group
 }
-
-
-// Parse config file options
-void
-fgParseOptions (const string& path) {
-    sg_gzifstream in( path );
-    if ( !in.is_open() ) {
-        return;
+  
+int Options::parseOption(const string& s)
+{
+  if ((s == "--help") || (s=="-h")) {
+    return FG_OPTIONS_HELP;
+  } else if ( (s == "--verbose") || (s == "-v") ) {
+    // verbose help/usage request
+    return FG_OPTIONS_VERBOSE_HELP;
+  } else if (s.find("-psn") == 0) {
+    // on Mac, when launched from the GUI, we are passed the ProcessSerialNumber
+    // as an argument (and no others). Silently ignore the argument here.
+    return FG_OPTIONS_OK;
+  } else if ( s.find( "--show-aircraft") == 0) {
+    return(FG_OPTIONS_SHOW_AIRCRAFT);
+  } else if ( s.find( "--show-sound-devices") == 0) {
+    return(FG_OPTIONS_SHOW_SOUND_DEVICES);
+  } else if ( s.find( "--prop:") == 0) {
+    // property setting has a slightly different syntax, so fudge things
+    OptionDesc* desc = p->findOption("prop");
+    if (s.find("=", 7) == string::npos) { // no equals token
+      SG_LOG(SG_GENERAL, SG_ALERT, "malformed property option:" << s);
+      return FG_OPTIONS_ERROR;
     }
-
-    SG_LOG( SG_GENERAL, SG_INFO, "Processing config file: " << path );
-
-    in >> skipcomment;
-    while ( ! in.eof() ) {
-       string line;
-       getline( in, line, '\n' );
-
-        // catch extraneous (DOS) line ending character
-        int i;
-        for (i = line.length(); i > 0; i--)
-            if (line[i - 1] > 32)
-                break;
-        line = line.substr( 0, i );
-
-       if ( parse_option( line ) == FG_OPTIONS_ERROR ) {
-            cerr << endl << "Config file parse error: " << path << " '" 
-                   << line << "'" << endl;
-           fgUsage();
-           exit(-1);
-       }
-       in >> skipcomment;
+    
+    p->values.push_back(OptionValue(desc, s.substr(7)));
+    return FG_OPTIONS_OK;
+  } else if ( s.find( "--" ) == 0 ) {
+    size_t eqPos = s.find( '=' );
+    string key, value;
+    if (eqPos == string::npos) {
+      key = s.substr(2);
+    } else {
+      key = s.substr( 2, eqPos - 2 );
+      value = s.substr( eqPos + 1);
     }
+    
+    return addOption(key, value);
+  } else {
+    SG_LOG(SG_GENERAL, SG_ALERT, "unknown option:" << s);
+    return FG_OPTIONS_ERROR;
+  }
 }
-
-
-// Print usage message
-void 
-fgUsage (bool verbose)
+  
+int Options::addOption(const string &key, const string &value)
 {
-    SGPropertyNode *locale = globals->get_locale();
-
-    SGPropertyNode options_root;
-
-    SG_LOG( SG_GENERAL, SG_ALERT, "" ); // To popup the console on Windows
-    cout << endl;
-
-    try {
-        fgLoadProps("options.xml", &options_root);
-    } catch (const sg_exception &) {
-        cout << "Unable to read the help file." << endl;
-        cout << "Make sure the file options.xml is located in the FlightGear base directory," << endl;
-        cout << "and the location of the base directory is specified by setting $FG_ROOT or" << endl;
-        cout << "by adding --fg-root=path as a program argument." << endl;
-        
-        exit(-1);
+  OptionDesc* desc = p->findOption(key);
+  if (!desc) {
+    SG_LOG(SG_GENERAL, SG_ALERT, "unknown option:" << key);
+    return FG_OPTIONS_ERROR;
+  }
+  
+  if (!(desc->type & OPTION_MULTI)) {
+    OptionValueVec::const_iterator it = p->findValue(key);
+    if (it != p->values.end()) {
+      SG_LOG(SG_GENERAL, SG_INFO, "multiple values forbidden for option:" << key << ", ignoring:" << value);
+      return FG_OPTIONS_OK;
     }
+  }
+  
+  p->values.push_back(OptionValue(desc, value));
+  return FG_OPTIONS_OK;
+}
+  
+bool Options::isOptionSet(const string &key) const
+{
+  OptionValueVec::const_iterator it = p->findValue(key);
+  return (it != p->values.end());
+}
+  
+string Options::valueForOption(const string& key, const string& defValue) const
+{
+  OptionValueVec::const_iterator it = p->findValue(key);
+  if (it == p->values.end()) {
+    return defValue;
+  }
+  
+  return it->value;
+}
 
-    SGPropertyNode *options = options_root.getNode("options");
-    if (!options) {
-        SG_LOG( SG_GENERAL, SG_ALERT,
-                "Error reading options.xml: <options> directive not found." );
-        exit(-1);
+string_list Options::valuesForOption(const std::string& key) const
+{
+  string_list result;
+  OptionValueVec::const_iterator it = p->values.begin();
+  for (; it != p->values.end(); ++it) {
+    if (!it->desc) {
+      continue; // ignore marker values
     }
-
-    SGPropertyNode *usage = locale->getNode(options->getStringValue("usage"));
-    if (usage) {
-        cout << "Usage: " << usage->getStringValue() << endl;
+    
+    if (it->desc->option == key) {
+      result.push_back(it->value);
+    }
+  }
+  
+  return result;
+}
+  
+void Options::processOptions()
+{
+  // now FG_ROOT is setup, process various command line options that bail us
+  // out quickly, but rely on aircraft / root settings
+  if (p->showHelp) {
+    showUsage();
+    exit(0);
+  }
+  
+  // processing order is complicated. We must process groups LIFO, but the
+  // values *within* each group in FIFO order, to retain consistency with
+  // older versions of FG, and existing user configs.
+  // in practice this means system.fgfsrc must be *processed* before
+  // .fgfsrc, which must be processed before the command line args, and so on.
+  OptionValueVec::const_iterator groupEnd = p->values.end();
+    
+  while (groupEnd != p->values.begin()) {
+    OptionValueVec::const_iterator groupBegin = p->rfindGroup(groupEnd);
+  // run over the group in FIFO order
+    OptionValueVec::const_iterator it;
+    for (it = groupBegin; it != groupEnd; ++it) {      
+      int result = p->processOption(it->desc, it->value);
+      switch(result)
+      {
+          case FG_OPTIONS_ERROR:
+              showUsage();
+              exit(-1); // exit and return an error
+          case FG_OPTIONS_EXIT:
+              exit(0);  // clean exit
+              break;
+          default:
+              break;
+      }
+    }
+    
+    groupEnd = groupBegin;
+  }
+  
+  BOOST_FOREACH(const SGPath& file, p->propertyFiles) {
+    if (!file.exists()) {
+      SG_LOG(SG_GENERAL, SG_ALERT, "config file not found:" << file.str());
+      continue;
     }
+    
+    SG_LOG(SG_GENERAL, SG_INFO,
+           "Reading command-line property file " << file.str());
+         readProperties(file.str(), globals->get_props());
+  }
 
-    vector<SGPropertyNode_ptr>section = options->getChildren("section");
-    for (unsigned int j = 0; j < section.size(); j++) {
-        string msg = "";
-
-        vector<SGPropertyNode_ptr>option = section[j]->getChildren("option");
-        for (unsigned int k = 0; k < option.size(); k++) {
-
-            SGPropertyNode *name = option[k]->getNode("name");
-            SGPropertyNode *short_name = option[k]->getNode("short");
-            SGPropertyNode *key = option[k]->getNode("key");
-            SGPropertyNode *arg = option[k]->getNode("arg");
-            bool brief = option[k]->getNode("brief") != 0;
-
-            if ((brief || verbose) && name) {
-                string tmp = name->getStringValue();
-
-                if (key){
-                    tmp.append(":");
-                    tmp.append(key->getStringValue());
-                }
-                if (arg) {
-                    tmp.append("=");
-                    tmp.append(arg->getStringValue());
-                }
-                if (short_name) {
-                    tmp.append(", -");
-                    tmp.append(short_name->getStringValue());
-                }
-                               
-                if (tmp.size() <= 25) {
-                    msg+= "   --";
-                    msg += tmp;
-                    msg.append( 27-tmp.size(), ' ');
-                } else {
-                    msg += "\n   --";
-                    msg += tmp + '\n';
-                    msg.append(32, ' ');
-                }
-                // There may be more than one <description> tag assosiated
-                // with one option
-
-                vector<SGPropertyNode_ptr> desc;
-                desc = option[k]->getChildren("description");
-                if (desc.size() > 0) {
-                   for ( unsigned int l = 0; l < desc.size(); l++) {
-
-                      // There may be more than one translation line.
-
-                      string t = desc[l]->getStringValue();
-                      SGPropertyNode *n = locale->getNode("strings");
-                      vector<SGPropertyNode_ptr>trans_desc =
-                               n->getChildren(t.substr(8).c_str());
-
-                      for ( unsigned int m = 0; m < trans_desc.size(); m++ ) {
-                         string t_str = trans_desc[m]->getStringValue();
-
-                         if ((m > 0) || ((l > 0) && m == 0)) {
-                            msg.append( 32, ' ');
-                         }
-
-                         // If the string is too large to fit on the screen,
-                         // then split it up in several pieces.
-
-                         while ( t_str.size() > 47 ) {
-
-                            unsigned int m = t_str.rfind(' ', 47);
-                            msg += t_str.substr(0, m) + '\n';
-                            msg.append( 32, ' ');
-
-                            t_str.erase(t_str.begin(), t_str.begin() + m + 1);
-                        }
-                        msg += t_str + '\n';
-                     }
-                  }
-               }
-            }
+// now options are process, do supplemental fixup
+  const char *envp = ::getenv( "FG_SCENERY" );
+  if (envp) {
+    globals->append_fg_scenery(envp);
+  }
+    
+// terrasync directory fixup
+  if (fgGetBool("/sim/terrasync/enabled")) {
+    string terrasyncDir = fgGetString("/sim/terrasync/scenery-dir");
+    if (terrasyncDir.empty()) {
+      SGPath p(fgGetString("/sim/fg-home"));
+      p.append("TerraSync");
+      if (!p.exists()) {
+        simgear::Dir dd(p);
+        dd.create(0700);
+      }
+      
+      terrasyncDir = p.str();
+      SG_LOG(SG_GENERAL, SG_INFO,
+             "Using default TerraSync dir: " << terrasyncDir);
+      fgSetString("/sim/terrasync/scenery-dir", terrasyncDir);
+    }
+    
+    const string_list& scenery_paths(globals->get_fg_scenery());
+    if (std::find(scenery_paths.begin(), scenery_paths.end(), terrasyncDir) == scenery_paths.end()) {
+      // terrasync dir is not in the scenery paths, add it
+      globals->append_fg_scenery(terrasyncDir);
+    }
+  }
+  
+  if (globals->get_fg_scenery().empty()) {
+    // no scenery paths set *at all*, use the data in FG_ROOT
+    SGPath root(globals->get_fg_root());
+    root.append("Scenery");
+    globals->append_fg_scenery(root.str());
+  }
+}
+  
+void Options::showUsage() const
+{
+  fgOptLogLevel( "alert" );
+  
+  SGPropertyNode *locale = globals->get_locale();
+  SGPropertyNode options_root;
+  
+  SG_LOG( SG_GENERAL, SG_ALERT, "" ); // To popup the console on Windows
+  cout << endl;
+  
+  try {
+    fgLoadProps("options.xml", &options_root);
+  } catch (const sg_exception &) {
+    cout << "Unable to read the help file." << endl;
+    cout << "Make sure the file options.xml is located in the FlightGear base directory," << endl;
+    cout << "and the location of the base directory is specified by setting $FG_ROOT or" << endl;
+    cout << "by adding --fg-root=path as a program argument." << endl;
+    
+    exit(-1);
+  }
+  
+  SGPropertyNode *options = options_root.getNode("options");
+  if (!options) {
+    SG_LOG( SG_GENERAL, SG_ALERT,
+           "Error reading options.xml: <options> directive not found." );
+    exit(-1);
+  }
+  
+  SGPropertyNode *usage = locale->getNode(options->getStringValue("usage"));
+  if (usage) {
+    cout << "Usage: " << usage->getStringValue() << endl;
+  }
+  
+  vector<SGPropertyNode_ptr>section = options->getChildren("section");
+  for (unsigned int j = 0; j < section.size(); j++) {
+    string msg = "";
+    
+    vector<SGPropertyNode_ptr>option = section[j]->getChildren("option");
+    for (unsigned int k = 0; k < option.size(); k++) {
+      
+      SGPropertyNode *name = option[k]->getNode("name");
+      SGPropertyNode *short_name = option[k]->getNode("short");
+      SGPropertyNode *key = option[k]->getNode("key");
+      SGPropertyNode *arg = option[k]->getNode("arg");
+      bool brief = option[k]->getNode("brief") != 0;
+      
+      if ((brief || p->verbose) && name) {
+        string tmp = name->getStringValue();
+        
+        if (key){
+          tmp.append(":");
+          tmp.append(key->getStringValue());
         }
-
-        SGPropertyNode *name;
-        name = locale->getNode(section[j]->getStringValue("name"));
-
-        if (!msg.empty() && name) {
-           cout << endl << name->getStringValue() << ":" << endl;
-           cout << msg;
-           msg.erase();
+        if (arg) {
+          tmp.append("=");
+          tmp.append(arg->getStringValue());
+        }
+        if (short_name) {
+          tmp.append(", -");
+          tmp.append(short_name->getStringValue());
+        }
+        
+        if (tmp.size() <= 25) {
+          msg+= "   --";
+          msg += tmp;
+          msg.append( 27-tmp.size(), ' ');
+        } else {
+          msg += "\n   --";
+          msg += tmp + '\n';
+          msg.append(32, ' ');
+        }
+        // There may be more than one <description> tag assosiated
+        // with one option
+        
+        vector<SGPropertyNode_ptr> desc;
+        desc = option[k]->getChildren("description");
+        if (desc.size() > 0) {
+          for ( unsigned int l = 0; l < desc.size(); l++) {
+            
+            // There may be more than one translation line.
+            
+            string t = desc[l]->getStringValue();
+            SGPropertyNode *n = locale->getNode("strings");
+            vector<SGPropertyNode_ptr>trans_desc =
+            n->getChildren(t.substr(8).c_str());
+            
+            for ( unsigned int m = 0; m < trans_desc.size(); m++ ) {
+              string t_str = trans_desc[m]->getStringValue();
+              
+              if ((m > 0) || ((l > 0) && m == 0)) {
+                msg.append( 32, ' ');
+              }
+              
+              // If the string is too large to fit on the screen,
+              // then split it up in several pieces.
+              
+              while ( t_str.size() > 47 ) {
+                
+                string::size_type m = t_str.rfind(' ', 47);
+                msg += t_str.substr(0, m) + '\n';
+                msg.append( 32, ' ');
+                
+                t_str.erase(t_str.begin(), t_str.begin() + m + 1);
+              }
+              msg += t_str + '\n';
+            }
+          }
         }
+      }
+    }
+    
+    SGPropertyNode *name;
+    name = locale->getNode(section[j]->getStringValue("name"));
+    
+    if (!msg.empty() && name) {
+      cout << endl << name->getStringValue() << ":" << endl;
+      cout << msg;
+      msg.erase();
     }
+  }
+  
+  if ( !p->verbose ) {
+    cout << endl;
+    cout << "For a complete list of options use --help --verbose" << endl;
+  }
+#ifdef _MSC_VER
+  std::cout << "Hit a key to continue..." << std::endl;
+  std::cin.get();
+#endif
+}
+  
+#if defined(__CYGWIN__)
+string Options::platformDefaultRoot() const
+{
+  return "../data";
+}
 
-    if ( !verbose ) {
-        cout << endl;
-        cout << "For a complete list of options use --help --verbose" << endl;
+#elif defined(_WIN32)
+string Options::platformDefaultRoot() const
+{
+  return "..\\data";
+}
+#elif defined(__APPLE__)
+string Options::platformDefaultRoot() const
+{
+  /*
+   The following code looks for the base package inside the application 
+   bundle, in the standard Contents/Resources location. 
+   */
+  CFURLRef resourcesUrl = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
+  
+  // look for a 'data' subdir
+  CFURLRef dataDir = CFURLCreateCopyAppendingPathComponent(NULL, resourcesUrl, CFSTR("data"), true);
+  
+  // now convert down to a path, and the a c-string
+  CFStringRef path = CFURLCopyFileSystemPath(dataDir, kCFURLPOSIXPathStyle);
+  string root = CFStringGetCStringPtr(path, CFStringGetSystemEncoding());
+  
+  CFRelease(resourcesUrl);
+  CFRelease(dataDir);
+  CFRelease(path);
+  
+  return root;
+}
+#else
+string Options::platformDefaultRoot() const
+{
+  return PKGLIBDIR;
+}
+#endif
+  
+void Options::setupRoot()
+{
+  string root;
+  if (isOptionSet("fg-root")) {
+    root = valueForOption("fg-root"); // easy!
+  } else {
+  // Next check if fg-root is set as an env variable
+    char *envp = ::getenv( "FG_ROOT" );
+    if ( envp != NULL ) {
+      root = envp;
+    } else {
+      root = platformDefaultRoot();
     }
+  } 
+  
+  SG_LOG(SG_INPUT, SG_INFO, "fg_root = " << root );
+  globals->set_fg_root(root);
+  
+// validate it
+  static char required_version[] = FLIGHTGEAR_VERSION;
+  string base_version = fgBasePackageVersion();
+  if ( !(base_version == required_version) ) {
+    // tell the operator how to use this application
+    
+    SG_LOG( SG_GENERAL, SG_ALERT, "" ); // To popup the console on windows
+    cerr << endl << "Base package check failed:" << endl \
+    << "  Version " << base_version << " found at: " \
+    << globals->get_fg_root() << endl \
+    << "  Version " << required_version << " is required." << endl \
+    << "Please upgrade/downgrade base package and set the path to your fgdata" << endl \
+    << "with --fg-root=path_to_your_fgdata" << endl;
 #ifdef _MSC_VER
-    cout << "Hit a key to continue..." << endl;
+    cerr << "Hit a key to continue..." << endl;
     cin.get();
 #endif
+    exit(-1);
+  }
 }
+  
+} // of namespace flightgear
+