]> git.mxchange.org Git - flightgear.git/blobdiff - src/Main/options.cxx
Refactor SGSky handling and ownership - sink into Renderer, remove global variable...
[flightgear.git] / src / Main / options.cxx
index d6ab0b7dffa08d7f58f2f397212c6e37b5123325..bcb5ccdad98cdb54fe941659ed60330e59dfbca0 100644 (file)
@@ -79,6 +79,7 @@ using std::sort;
 using std::cout;
 using std::cerr;
 using std::endl;
+using std::vector;
 
 #define NEW_DEFAULT_MODEL_HZ 120
 
@@ -182,7 +183,7 @@ fgSetDefaults ()
 #elif defined(sgi)
     fgSetString("/sim/startup/browser-app", "launchWebJumper");
 #else
-    char* browserEnv = ::getenv( "WEBBROWSER" );
+    const char* browserEnv = ::getenv( "WEBBROWSER" );
     if (!browserEnv) browserEnv = "netscape";
     fgSetString("/sim/startup/browser-app", browserEnv);
 #endif
@@ -282,6 +283,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) {
@@ -363,112 +414,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)
+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);
 }
 
@@ -485,9 +487,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);
 
@@ -810,7 +812,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;
@@ -819,24 +821,36 @@ 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;
 }
 
@@ -1456,20 +1470,20 @@ struct OptionDesc {
 #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 },
-    {"AV400WSimA",                   true,  OPTION_CHANNEL, "", false, "", 0 },
-    {"AV400WSimB",                   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 },
@@ -1477,12 +1491,12 @@ 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 },
+    {"multiplay",                    true,  OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
 #ifdef FG_HAVE_HLA
     {"hla",                          true,  OPTION_CHANNEL, "", false, "", 0 },
 #endif
-    {"trace-read",                   true,  OPTION_FUNC,   "", false, "", fgOptTraceRead },
-    {"trace-write",                  true,  OPTION_FUNC,   "", false, "", fgOptTraceWrite },
+    {"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 | OPTION_MULTI,   "", false, "", fgOptViewOffset },
     {"visibility",                   true,  OPTION_FUNC,   "", false, "", fgOptVisibilityMeters },
@@ -1661,7 +1675,8 @@ Options::Options() :
 // build option map
   OptionDesc *desc = &fgOptionArray[ 0 ];
   while ( desc->option != 0 ) {
-    p->options[ desc->option ] = desc++;
+    p->options[ desc->option ] = desc;
+    ++desc;
   }
 }
   
@@ -1669,7 +1684,7 @@ Options::~Options()
 {
 }
   
-void Options::init(int argc, char **argv)
+void Options::init(int argc, char **argv, const SGPath& appDataPath)
 {
   fgSetDefaults();
   
@@ -1715,6 +1730,13 @@ void Options::init(int argc, char **argv)
     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();
   
@@ -1929,14 +1951,17 @@ void Options::processOptions()
     exit(0);
   }
   
-  BOOST_FOREACH(const OptionValue& v, p->values) {
-    int result = p->processOption(v.desc, v.value);
+  // proces options in LIFO order, so earlier (first in) options are processed
+  // after, and hence override, later specified options.
+  OptionValueVec::const_reverse_iterator it = p->values.rbegin();
+  for (; it != p->values.rend(); ++it) {
+    int result = p->processOption(it->desc, it->value);
     if (result == FG_OPTIONS_ERROR) {
       showUsage();
       exit(-1);
     }
   }
-  
+
   BOOST_FOREACH(const SGPath& file, p->propertyFiles) {
     if (!file.exists()) {
       SG_LOG(SG_GENERAL, SG_ALERT, "config file not found:" << file.str());
@@ -1953,9 +1978,33 @@ void Options::processOptions()
   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
+    // 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());