1 // options.cxx -- class to handle command line options
3 // Written by Curtis Olson, started April 1998.
5 // Copyright (C) 1998 Curtis L. Olson - http://www.flightgear.org/~curt
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 #include <simgear/compiler.h>
29 #include <simgear/structure/exception.hxx>
30 #include <simgear/debug/logstream.hxx>
31 #include <simgear/timing/sg_time.hxx>
32 #include <simgear/misc/sg_dir.hxx>
34 #include <boost/foreach.hpp>
36 #include <cmath> // rint()
38 #include <stdlib.h> // atof(), atoi()
39 #include <string.h> // strcmp()
46 #include <simgear/math/sg_random.h>
47 #include <simgear/props/props_io.hxx>
48 #include <simgear/misc/sgstream.hxx>
49 #include <simgear/misc/sg_path.hxx>
50 #include <simgear/scene/material/mat.hxx>
51 #include <simgear/sound/soundmgr_openal.hxx>
52 #include <simgear/misc/strutils.hxx>
53 #include <Autopilot/route_mgr.hxx>
54 #include <Aircraft/replay.hxx>
55 #include <Aircraft/initialstate.hxx>
58 #include <GUI/MessageBox.hxx>
60 #include <GUI/QtLauncher.hxx>
61 #include <GUI/SetupRootDialog.hxx>
64 #include <Main/locale.hxx>
65 #include "globals.hxx"
66 #include "fg_init.hxx"
67 #include "fg_props.hxx"
68 #include "options.hxx"
72 #include <Viewer/view.hxx>
73 #include <Viewer/viewmgr.hxx>
74 #include <Environment/presets.hxx>
75 #include <Network/http/httpd.hxx>
76 #include "AircraftDirVisitorBase.hxx"
78 #include <osg/Version>
79 #include <Include/version.h>
80 #include <simgear/version.h>
90 using namespace flightgear;
92 #define NEW_DEFAULT_MODEL_HZ 120
94 static flightgear::Options* shared_instance = NULL;
97 atof( const string& str )
99 return ::atof( str.c_str() );
103 atoi( const string& str )
105 return ::atoi( str.c_str() );
108 static int fgSetupProxy( const char *arg );
111 * Set a few fail-safe default property values.
113 * These should all be set in $FG_ROOT/preferences.xml, but just
114 * in case, we provide some initial sane values here. This method
115 * should be invoked *before* reading any init files.
117 void fgSetDefaults ()
120 // Position (deliberately out of range)
121 fgSetDouble("/position/longitude-deg", 9999.0);
122 fgSetDouble("/position/latitude-deg", 9999.0);
123 fgSetDouble("/position/altitude-ft", -9999.0);
126 fgSetDouble("/orientation/heading-deg", 9999.0);
127 fgSetDouble("/orientation/roll-deg", 0.0);
128 fgSetDouble("/orientation/pitch-deg", 0.424);
131 fgSetDouble("/velocities/uBody-fps", 0.0);
132 fgSetDouble("/velocities/vBody-fps", 0.0);
133 fgSetDouble("/velocities/wBody-fps", 0.0);
134 fgSetDouble("/velocities/speed-north-fps", 0.0);
135 fgSetDouble("/velocities/speed-east-fps", 0.0);
136 fgSetDouble("/velocities/speed-down-fps", 0.0);
137 fgSetDouble("/velocities/airspeed-kt", 0.0);
138 fgSetDouble("/velocities/mach", 0.0);
141 fgSetDouble("/sim/presets/longitude-deg", 9999.0);
142 fgSetDouble("/sim/presets/latitude-deg", 9999.0);
143 fgSetDouble("/sim/presets/altitude-ft", -9999.0);
145 fgSetDouble("/sim/presets/heading-deg", 9999.0);
146 fgSetDouble("/sim/presets/roll-deg", 0.0);
147 fgSetDouble("/sim/presets/pitch-deg", 0.424);
149 fgSetString("/sim/presets/speed-set", "knots");
150 fgSetDouble("/sim/presets/airspeed-kt", 0.0);
151 fgSetDouble("/sim/presets/mach", 0.0);
152 fgSetDouble("/sim/presets/uBody-fps", 0.0);
153 fgSetDouble("/sim/presets/vBody-fps", 0.0);
154 fgSetDouble("/sim/presets/wBody-fps", 0.0);
155 fgSetDouble("/sim/presets/speed-north-fps", 0.0);
156 fgSetDouble("/sim/presets/speed-east-fps", 0.0);
157 fgSetDouble("/sim/presets/speed-down-fps", 0.0);
159 fgSetBool("/sim/presets/onground", true);
160 fgSetBool("/sim/presets/trim", false);
163 fgSetBool("/sim/startup/splash-screen", true);
164 // we want mouse-pointer to have an undefined value if nothing is
165 // specified so we can do the right thing for voodoo-1/2 cards.
166 // fgSetString("/sim/startup/mouse-pointer", "disabled");
167 fgSetBool("/controls/flight/auto-coordination", false);
168 fgSetString("/sim/logging/priority", "alert");
171 fgSetBool("/sim/hud/color/antialiased", false);
172 fgSetBool("/sim/hud/enable3d[1]", true);
173 fgSetBool("/sim/hud/visibility[1]", false);
174 fgSetBool("/sim/panel/visibility", true);
175 fgSetBool("/sim/sound/enabled", true);
176 fgSetBool("/sim/sound/working", true);
177 fgSetBool("/sim/fgcom/enabled", false);
179 // Flight Model options
180 fgSetString("/sim/flight-model", "jsb");
181 fgSetString("/sim/aero", "c172");
182 fgSetInt("/sim/model-hz", NEW_DEFAULT_MODEL_HZ);
183 fgSetDouble("/sim/speed-up", 1.0);
186 fgSetString("/sim/rendering/fog", "nicest");
187 fgSetBool("/environment/clouds/status", true);
188 fgSetBool("/sim/startup/fullscreen", false);
189 fgSetBool("/sim/rendering/shading", true);
190 fgTie( "/sim/rendering/filtering", SGGetTextureFilter, SGSetTextureFilter, false);
191 fgSetInt("/sim/rendering/filtering", 1);
192 fgSetBool("/sim/rendering/wireframe", false);
193 fgSetBool("/sim/rendering/horizon-effect", false);
194 fgSetBool("/sim/rendering/enhanced-lighting", false);
195 fgSetBool("/sim/rendering/distance-attenuation", false);
196 fgSetBool("/sim/rendering/specular-highlight", true);
197 fgSetString("/sim/rendering/materials-file", "materials.xml");
198 fgSetInt("/sim/startup/xsize", 1024);
199 fgSetInt("/sim/startup/ysize", 768);
200 fgSetInt("/sim/rendering/bits-per-pixel", 32);
201 fgSetString("/sim/view-mode", "pilot");
202 fgSetDouble("/sim/current-view/heading-offset-deg", 0);
205 fgSetString("/sim/startup/units", "feet");
206 fgSetString("/sim/hud/frame-stat-type", "tris");
209 fgSetInt("/sim/startup/time-offset", 0);
210 fgSetString("/sim/startup/time-offset-type", "system-offset");
211 fgSetLong("/sim/time/cur-time-override", 0);
214 fgSetBool("/sim/freeze/master", false);
215 fgSetBool("/sim/freeze/position", false);
216 fgSetBool("/sim/freeze/clock", false);
217 fgSetBool("/sim/freeze/fuel", false);
219 fgSetString("/sim/multiplay/callsign", "callsign");
220 fgSetString("/sim/multiplay/rxhost", "");
221 fgSetString("/sim/multiplay/txhost", "");
222 fgSetInt("/sim/multiplay/rxport", 0);
223 fgSetInt("/sim/multiplay/txport", 0);
225 SGPropertyNode* v = globals->get_props()->getNode("/sim/version", true);
226 v->setValueReadOnly("flightgear", FLIGHTGEAR_VERSION);
227 v->setValueReadOnly("simgear", SG_STRINGIZE(SIMGEAR_VERSION));
228 v->setValueReadOnly("openscenegraph", osgGetVersion());
229 v->setValueReadOnly("openscenegraph-thread-safe-reference-counting",
230 osg::Referenced::getThreadSafeReferenceCounting());
231 v->setValueReadOnly("revision", REVISION);
232 v->setValueReadOnly("build-number", HUDSON_BUILD_NUMBER);
233 v->setValueReadOnly("build-id", HUDSON_BUILD_ID);
234 v->setValueReadOnly("hla-support", bool(FG_HAVE_HLA));
235 #if defined(FG_NIGHTLY)
236 v->setValueReadOnly("nightly-build", true);
238 v->setValueReadOnly("nightly-build", false);
241 char* envp = ::getenv( "http_proxy" );
243 fgSetupProxy( envp );
246 ///////////////////////////////////////////////////////////////////////////////
247 // helper object to implement the --show-aircraft command.
248 // resides here so we can share the fgFindAircraftInDir template above,
249 // and hence ensure this command lists exectly the same aircraft as the normal
251 class ShowAircraft : public AircraftDirVistorBase
256 _minStatus = getNumMaturity(fgGetString("/sim/aircraft-min-status", "all"));
260 void show(const vector<SGPath> & path_list)
262 for (vector<SGPath>::const_iterator p = path_list.begin();
263 p != path_list.end(); ++p)
266 simgear::requestConsole(); // ensure console is shown on Windows
268 std::sort(_aircraft.begin(), _aircraft.end(), ciLessLibC());
269 cout << "Available aircraft:" << endl;
270 for ( unsigned int i = 0; i < _aircraft.size(); i++ ) {
271 cout << _aircraft[i] << endl;
276 virtual VisitResult visit(const SGPath& path)
280 readProperties(path, &root);
281 } catch (sg_exception& ) {
282 return VISIT_CONTINUE;
287 descStr += path.file();
288 // trim common suffix from file names
289 int nPos = descStr.rfind("-set.xml");
290 if (nPos == (int)(descStr.size() - 8)) {
291 descStr.resize(nPos);
294 SGPropertyNode *node = root.getNode("sim");
296 SGPropertyNode* desc = node->getNode("description");
297 // if a status tag is found, read it in
298 if (node->hasValue("status")) {
299 maturity = getNumMaturity(node->getStringValue("status"));
303 if (descStr.size() <= 27+3) {
304 descStr.append(29+3-descStr.size(), ' ');
307 descStr.append( 32, ' ');
309 descStr += desc->getStringValue();
311 } // of have 'sim' node
313 if (maturity >= _minStatus) {
314 _aircraft.push_back(descStr);
317 return VISIT_CONTINUE;
321 int getNumMaturity(const char * str)
323 // changes should also be reflected in $FG_ROOT/data/options.xml &
324 // $FG_ROOT/data/Translations/string-default.xml
325 const char* levels[] = {"alpha","beta","early-production","production"};
327 if (!strcmp(str, "all")) {
331 for (size_t i=0; i<(sizeof(levels)/sizeof(levels[0]));i++)
332 if (strcmp(str,levels[i])==0)
338 // recommended in Meyers, Effective STL when internationalization and embedded
339 // NULLs aren't an issue. Much faster than the STL or Boost lex versions.
340 struct ciLessLibC : public std::binary_function<string, string, bool>
342 bool operator()(const std::string &lhs, const std::string &rhs) const
344 return strcasecmp(lhs.c_str(), rhs.c_str()) < 0 ? 1 : 0;
349 string_list _aircraft;
353 * Search in the current directory, and in on directory deeper
354 * for <aircraft>-set.xml configuration files and show the aircaft name
355 * and the contents of the<description> tag in a sorted manner.
357 * @parampath the directory to search for configuration files
359 void fgShowAircraft(const vector<SGPath> &path_list)
365 cout << "Hit a key to continue..." << endl;
372 parse_wind (const string &wind, double * min_hdg, double * max_hdg,
373 double * speed, double * gust)
375 string::size_type pos = wind.find('@');
376 if (pos == string::npos)
378 string dir = wind.substr(0, pos);
379 string spd = wind.substr(pos+1);
381 if (pos == string::npos) {
382 *min_hdg = *max_hdg = atof(dir.c_str());
384 *min_hdg = atof(dir.substr(0,pos).c_str());
385 *max_hdg = atof(dir.substr(pos+1).c_str());
388 if (pos == string::npos) {
389 *speed = *gust = atof(spd.c_str());
391 *speed = atof(spd.substr(0,pos).c_str());
392 *gust = atof(spd.substr(pos+1).c_str());
398 parseIntValue(char** ppParserPos, int* pValue,int min, int max, const char* field, const char* argument)
400 if ( !strlen(*ppParserPos) )
406 while ( isdigit((*ppParserPos)[0]) && (i<255) )
408 num[i] = (*ppParserPos)[0];
414 switch ((*ppParserPos)[0])
422 SG_LOG(SG_GENERAL, SG_ALERT, "Illegal character in time string for " << field << ": '" <<
423 (*ppParserPos)[0] << "'.");
424 // invalid field - skip rest of string to avoid further errors
425 while ((*ppParserPos)[0])
433 int value = atoi(num);
434 if ((value < min)||(value > max))
436 SG_LOG(SG_GENERAL, SG_ALERT, "Invalid " << field << " in '" << argument <<
437 "'. Valid range is " << min << "-" << max << ".");
447 // parse a time string ([+/-]%f[:%f[:%f]]) into hours
449 parse_time(const string& time_in) {
450 char *time_str, num[256];
451 double hours, minutes, seconds;
456 time_str = (char *)time_in.c_str();
458 // printf("parse_time(): %s\n", time_str);
461 if ( strlen(time_str) ) {
462 if ( time_str[0] == '+' ) {
465 } else if ( time_str[0] == '-' ) {
470 // printf("sign = %d\n", sign);
473 if ( strlen(time_str) ) {
475 while ( (time_str[0] != ':') && (time_str[0] != '\0') ) {
476 num[i] = time_str[0];
480 if ( time_str[0] == ':' ) {
485 // printf("hours = %.2lf\n", hours);
491 if ( strlen(time_str) ) {
493 while ( (time_str[0] != ':') && (time_str[0] != '\0') ) {
494 num[i] = time_str[0];
498 if ( time_str[0] == ':' ) {
503 // printf("minutes = %.2lf\n", minutes);
505 result += minutes / 60.0;
509 if ( strlen(time_str) ) {
511 while ( (time_str[0] != ':') && (time_str[0] != '\0') ) {
512 num[i] = time_str[0];
518 // printf("seconds = %.2lf\n", seconds);
520 result += seconds / 3600.0;
523 SG_LOG( SG_GENERAL, SG_INFO, " parse_time() = " << sign * result );
525 return(sign * result);
528 // parse a date string (yyyy:mm:dd:hh:mm:ss) into a time_t (seconds)
530 parse_date( const string& date, const char* timeType)
532 struct tm gmt,*pCurrentTime;
533 int year,month,day,hour,minute,second;
534 char *argument, *date_str;
537 CurrentTime.update(SGGeod(),0,0);
539 // FIXME This should obtain system/aircraft/GMT time depending on timeType
540 pCurrentTime = CurrentTime.getGmt();
542 // initialize all fields with current time
543 year = pCurrentTime->tm_year + 1900;
544 month = pCurrentTime->tm_mon + 1;
545 day = pCurrentTime->tm_mday;
546 hour = pCurrentTime->tm_hour;
547 minute = pCurrentTime->tm_min;
548 second = pCurrentTime->tm_sec;
550 argument = (char *)date.c_str();
553 // start with parsing year
554 if (!strlen(date_str) ||
555 !parseIntValue(&date_str,&year,0,9999,"year",argument))
562 SG_LOG(SG_GENERAL, SG_ALERT, "Invalid year '" << year << "'. Use 1970 or later.");
566 parseIntValue(&date_str, &month, 1, 12, "month", argument);
567 parseIntValue(&date_str, &day, 1, 31, "day", argument);
568 parseIntValue(&date_str, &hour, 0, 23, "hour", argument);
569 parseIntValue(&date_str, &minute, 0, 59, "minute", argument);
570 parseIntValue(&date_str, &second, 0, 59, "second", argument);
576 gmt.tm_mon = month - 1;
577 gmt.tm_year = year -1900;
578 gmt.tm_isdst = 0; // ignore daylight savings time for the moment
580 time_t theTime = sgTimeGetGMT( gmt.tm_year, gmt.tm_mon, gmt.tm_mday,
581 gmt.tm_hour, gmt.tm_min, gmt.tm_sec );
583 SG_LOG(SG_GENERAL, SG_INFO, "Configuring startup time to " << ctime(&theTime));
589 // parse angle in the form of [+/-]ddd:mm:ss into degrees
591 parse_degree( const string& degree_str) {
592 double result = parse_time( degree_str );
594 // printf("Degree = %.4f\n", result);
600 // parse time offset string into seconds
602 parse_time_offset( const string& time_str) {
605 // printf("time offset = %s\n", time_str);
608 result = (int)rint(parse_time(time_str) * 3600.0);
610 result = (int)(parse_time(time_str) * 3600.0);
613 // printf("parse_time_offset(): %d\n", result);
619 // Parse --fov=x.xx type option
621 parse_fov( const string& arg ) {
622 double fov = atof(arg);
624 if ( fov < FG_FOV_MIN ) { fov = FG_FOV_MIN; }
625 if ( fov > FG_FOV_MAX ) { fov = FG_FOV_MAX; }
627 fgSetDouble("/sim/view[0]/config/default-field-of-view-deg", fov);
629 // printf("parse_fov(): result = %.4f\n", fov);
635 // Parse I/O channel option
637 // Format is "--protocol=medium,direction,hz,medium_options,..."
639 // protocol = { native, nmea, garmin, AV400, AV400Sim, fgfs, rul, pve, etc. }
640 // medium = { serial, socket, file, etc. }
641 // direction = { in, out, bi }
642 // hz = number of times to process channel per second (floating
643 // point values are ok.
645 // Serial example "--nmea=serial,dir,hz,device,baud" where
647 // device = OS device name of serial line to be open()'ed
648 // baud = {300, 1200, 2400, ..., 230400}
650 // Socket exacmple "--native=socket,dir,hz,machine,port,style" where
652 // machine = machine name or ip address if client (leave empty if server)
653 // port = port, leave empty to let system choose
654 // style = tcp or udp
656 // File example "--garmin=file,dir,hz,filename" where
658 // filename = file system file name
661 add_channel( const string& type, const string& channel_str ) {
662 // This check is neccessary to prevent fgviewer from segfaulting when given
663 // weird options. (It doesn't run the full initailization)
664 if(!globals->get_channel_options_list())
666 SG_LOG(SG_GENERAL, SG_ALERT, "Option " << type << "=" << channel_str
670 SG_LOG(SG_GENERAL, SG_INFO, "Channel string = " << channel_str );
671 globals->get_channel_options_list()->push_back( type + "," + channel_str );
678 fgSetString("/sim/presets/airport-id", "");
679 fgSetString("/sim/presets/vor-id", "");
680 fgSetString("/sim/presets/ndb-id", "");
681 fgSetString("/sim/presets/carrier", "");
682 fgSetString("/sim/presets/parkpos", "");
683 fgSetString("/sim/presets/fix", "");
687 fgOptVOR( const char * arg )
690 fgSetString("/sim/presets/vor-id", arg);
691 return FG_OPTIONS_OK;
695 fgOptNDB( const char * arg )
698 fgSetString("/sim/presets/ndb-id", arg);
699 return FG_OPTIONS_OK;
703 fgOptCarrier( const char * arg )
706 fgSetString("/sim/presets/carrier", arg);
707 return FG_OPTIONS_OK;
711 fgOptParkpos( const char * arg )
713 fgSetString("/sim/presets/parkpos", arg);
714 return FG_OPTIONS_OK;
718 fgOptFIX( const char * arg )
721 fgSetString("/sim/presets/fix", arg);
722 return FG_OPTIONS_OK;
726 fgOptLon( const char *arg )
729 fgSetDouble("/sim/presets/longitude-deg", parse_degree( arg ));
730 fgSetDouble("/position/longitude-deg", parse_degree( arg ));
731 return FG_OPTIONS_OK;
735 fgOptLat( const char *arg )
738 fgSetDouble("/sim/presets/latitude-deg", parse_degree( arg ));
739 fgSetDouble("/position/latitude-deg", parse_degree( arg ));
740 return FG_OPTIONS_OK;
744 fgOptAltitude( const char *arg )
746 fgSetBool("/sim/presets/onground", false);
747 if ( !strcmp(fgGetString("/sim/startup/units"), "feet") )
748 fgSetDouble("/sim/presets/altitude-ft", atof( arg ));
750 fgSetDouble("/sim/presets/altitude-ft",
751 atof( arg ) * SG_METER_TO_FEET);
752 return FG_OPTIONS_OK;
756 fgOptUBody( const char *arg )
758 fgSetString("/sim/presets/speed-set", "UVW");
759 if ( !strcmp(fgGetString("/sim/startup/units"), "feet") )
760 fgSetDouble("/sim/presets/uBody-fps", atof( arg ));
762 fgSetDouble("/sim/presets/uBody-fps",
763 atof( arg ) * SG_METER_TO_FEET);
764 return FG_OPTIONS_OK;
768 fgOptVBody( const char *arg )
770 fgSetString("/sim/presets/speed-set", "UVW");
771 if ( !strcmp(fgGetString("/sim/startup/units"), "feet") )
772 fgSetDouble("/sim/presets/vBody-fps", atof( arg ));
774 fgSetDouble("/sim/presets/vBody-fps",
775 atof( arg ) * SG_METER_TO_FEET);
776 return FG_OPTIONS_OK;
780 fgOptWBody( const char *arg )
782 fgSetString("/sim/presets/speed-set", "UVW");
783 if ( !strcmp(fgGetString("/sim/startup/units"), "feet") )
784 fgSetDouble("/sim/presets/wBody-fps", atof(arg));
786 fgSetDouble("/sim/presets/wBody-fps",
787 atof(arg) * SG_METER_TO_FEET);
788 return FG_OPTIONS_OK;
792 fgOptVNorth( const char *arg )
794 fgSetString("/sim/presets/speed-set", "NED");
795 if ( !strcmp(fgGetString("/sim/startup/units"), "feet") )
796 fgSetDouble("/sim/presets/speed-north-fps", atof( arg ));
798 fgSetDouble("/sim/presets/speed-north-fps",
799 atof( arg ) * SG_METER_TO_FEET);
800 return FG_OPTIONS_OK;
804 fgOptVEast( const char *arg )
806 fgSetString("/sim/presets/speed-set", "NED");
807 if ( !strcmp(fgGetString("/sim/startup/units"), "feet") )
808 fgSetDouble("/sim/presets/speed-east-fps", atof(arg));
810 fgSetDouble("/sim/presets/speed-east-fps",
811 atof(arg) * SG_METER_TO_FEET);
812 return FG_OPTIONS_OK;
816 fgOptVDown( const char *arg )
818 fgSetString("/sim/presets/speed-set", "NED");
819 if ( !strcmp(fgGetString("/sim/startup/units"), "feet") )
820 fgSetDouble("/sim/presets/speed-down-fps", atof(arg));
822 fgSetDouble("/sim/presets/speed-down-fps",
823 atof(arg) * SG_METER_TO_FEET);
824 return FG_OPTIONS_OK;
828 fgOptVc( const char *arg )
830 // fgSetString("/sim/presets/speed-set", "knots");
831 // fgSetDouble("/velocities/airspeed-kt", atof(arg.substr(5)));
832 fgSetString("/sim/presets/speed-set", "knots");
833 fgSetDouble("/sim/presets/airspeed-kt", atof(arg));
834 return FG_OPTIONS_OK;
838 fgOptMach( const char *arg )
840 fgSetString("/sim/presets/speed-set", "mach");
841 fgSetDouble("/sim/presets/mach", atof(arg));
842 return FG_OPTIONS_OK;
846 fgOptRoc( const char *arg )
848 fgSetDouble("/sim/presets/vertical-speed-fps", atof(arg)/60);
849 return FG_OPTIONS_OK;
853 fgOptFgScenery( const char *arg )
855 globals->append_fg_scenery(SGPath::pathsFromLocal8Bit(arg), true);
856 return FG_OPTIONS_OK;
860 fgOptTerrasyncDir( const char *arg )
862 SGPath p = SGPath::fromLocal8Bit(arg);
863 globals->append_fg_scenery(p, true);
864 fgSetString("/sim/terrasync/scenery-dir", p.utf8Str());
865 return FG_OPTIONS_OK;
869 fgOptFov( const char *arg )
872 return FG_OPTIONS_OK;
876 fgOptGeometry( const char *arg )
878 bool geometry_ok = true;
879 int xsize = 0, ysize = 0;
880 string geometry = arg;
881 string::size_type i = geometry.find('x');
883 if (i != string::npos) {
884 xsize = atoi(geometry.substr(0, i));
885 ysize = atoi(geometry.substr(i+1));
890 if ( xsize <= 0 || ysize <= 0 ) {
896 if ( !geometry_ok ) {
897 SG_LOG( SG_GENERAL, SG_ALERT, "Unknown geometry: " << geometry );
898 SG_LOG( SG_GENERAL, SG_ALERT,
899 "Setting geometry to " << xsize << 'x' << ysize << '\n');
901 SG_LOG( SG_GENERAL, SG_INFO,
902 "Setting geometry to " << xsize << 'x' << ysize << '\n');
903 fgSetInt("/sim/startup/xsize", xsize);
904 fgSetInt("/sim/startup/ysize", ysize);
906 return FG_OPTIONS_OK;
910 fgOptBpp( const char *arg )
912 string bits_per_pix = arg;
913 if ( bits_per_pix == "16" ) {
914 fgSetInt("/sim/rendering/bits-per-pixel", 16);
915 } else if ( bits_per_pix == "24" ) {
916 fgSetInt("/sim/rendering/bits-per-pixel", 24);
917 } else if ( bits_per_pix == "32" ) {
918 fgSetInt("/sim/rendering/bits-per-pixel", 32);
920 SG_LOG(SG_GENERAL, SG_ALERT, "Unsupported bpp " << bits_per_pix);
922 return FG_OPTIONS_OK;
926 fgOptTimeOffset( const char *arg )
928 fgSetLong("/sim/startup/time-offset",
929 parse_time_offset( arg ));
930 fgSetString("/sim/startup/time-offset-type", "system-offset");
931 return FG_OPTIONS_OK;
935 fgOptStartDateSys( const char *arg )
937 long int theTime = parse_date( arg, "system" );
940 fgSetLong("/sim/startup/time-offset", theTime);
941 fgSetString("/sim/startup/time-offset-type", "system");
943 return FG_OPTIONS_OK;
947 fgOptStartDateLat( const char *arg )
949 long int theTime = parse_date( arg, "latitude" );
952 fgSetLong("/sim/startup/time-offset", theTime);
953 fgSetString("/sim/startup/time-offset-type", "latitude");
955 return FG_OPTIONS_OK;
959 fgOptStartDateGmt( const char *arg )
961 long int theTime = parse_date( arg, "gmt" );
964 fgSetLong("/sim/startup/time-offset", theTime);
965 fgSetString("/sim/startup/time-offset-type", "gmt");
967 return FG_OPTIONS_OK;
971 fgOptJpgHttpd( const char * arg )
973 SG_LOG(SG_ALL,SG_ALERT,
974 "the option --jpg-httpd is no longer supported! Please use --httpd instead."
975 " URL for the screenshot within the new httpd is http://YourFgServer:xxxx/screenshot");
976 return FG_OPTIONS_EXIT;
980 fgOptHttpd( const char * arg )
982 // port may be any valid address:port notation
983 // like 127.0.0.1:8080
984 // or just the port 8080
985 string port = simgear::strutils::strip(string(arg));
986 if( port.empty() ) return FG_OPTIONS_ERROR;
987 fgSetString( string(flightgear::http::PROPERTY_ROOT).append("/options/listening-port").c_str(), port );
988 return FG_OPTIONS_OK;
992 fgSetupProxy( const char *arg )
994 string options = simgear::strutils::strip( arg );
995 string host, port, auth;
996 string::size_type pos;
998 // this is NURLP - NURLP is not an url parser
999 if( simgear::strutils::starts_with( options, "http://" ) )
1000 options = options.substr( 7 );
1001 if( simgear::strutils::ends_with( options, "/" ) )
1002 options = options.substr( 0, options.length() - 1 );
1004 host = port = auth = "";
1005 if ((pos = options.find("@")) != string::npos)
1006 auth = options.substr(0, pos++);
1010 host = options.substr(pos, options.size());
1011 if ((pos = host.find(":")) != string::npos) {
1012 port = host.substr(++pos, host.size());
1013 host.erase(--pos, host.size());
1016 fgSetString("/sim/presets/proxy/host", host.c_str());
1017 fgSetString("/sim/presets/proxy/port", port.c_str());
1018 fgSetString("/sim/presets/proxy/authentication", auth.c_str());
1020 return FG_OPTIONS_OK;
1024 fgOptTraceRead( const char *arg )
1027 SG_LOG(SG_GENERAL, SG_INFO, "Tracing reads for property " << name);
1028 fgGetNode(name.c_str(), true)
1029 ->setAttribute(SGPropertyNode::TRACE_READ, true);
1030 return FG_OPTIONS_OK;
1034 fgOptLogLevel( const char *arg )
1036 fgSetString("/sim/logging/priority", arg);
1037 setLoggingPriority(arg);
1039 return FG_OPTIONS_OK;
1043 fgOptLogClasses( const char *arg )
1045 fgSetString("/sim/logging/classes", arg);
1046 setLoggingClasses (arg);
1048 return FG_OPTIONS_OK;
1052 fgOptTraceWrite( const char *arg )
1055 SG_LOG(SG_GENERAL, SG_INFO, "Tracing writes for property " << name);
1056 fgGetNode(name.c_str(), true)
1057 ->setAttribute(SGPropertyNode::TRACE_WRITE, true);
1058 return FG_OPTIONS_OK;
1062 fgOptViewOffset( const char *arg )
1064 // $$$ begin - added VS Renganathan, 14 Oct 2K
1065 // for multi-window outside window imagery
1066 string woffset = arg;
1067 double default_view_offset = 0.0;
1068 if ( woffset == "LEFT" ) {
1069 default_view_offset = SGD_PI * 0.25;
1070 } else if ( woffset == "RIGHT" ) {
1071 default_view_offset = SGD_PI * 1.75;
1072 } else if ( woffset == "CENTER" ) {
1073 default_view_offset = 0.00;
1075 default_view_offset = atof( woffset.c_str() ) * SGD_DEGREES_TO_RADIANS;
1077 /* apparently not used (CLO, 11 Jun 2002)
1078 FGViewer *pilot_view =
1079 (FGViewer *)globals->get_viewmgr()->get_view( 0 ); */
1080 // this will work without calls to the viewer...
1081 fgSetDouble( "/sim/current-view/heading-offset-deg",
1082 default_view_offset * SGD_RADIANS_TO_DEGREES );
1083 // $$$ end - added VS Renganathan, 14 Oct 2K
1084 return FG_OPTIONS_OK;
1088 fgOptVisibilityMeters( const char *arg )
1090 Environment::Presets::VisibilitySingleton::instance()->preset( atof( arg ) );
1091 return FG_OPTIONS_OK;
1095 fgOptVisibilityMiles( const char *arg )
1097 Environment::Presets::VisibilitySingleton::instance()->preset( atof( arg ) * 5280.0 * SG_FEET_TO_METER );
1098 return FG_OPTIONS_OK;
1102 fgOptMetar( const char *arg )
1104 // The given METAR string cannot be effective without disabling
1105 // real weather fetching.
1106 fgSetBool("/environment/realwx/enabled", false);
1107 // The user-supplied METAR string
1108 fgSetString("/environment/metar/data", arg);
1110 return FG_OPTIONS_OK;
1114 fgOptRandomWind( const char *arg )
1116 double min_hdg = sg_random() * 360.0;
1117 double max_hdg = min_hdg + (20 - sqrt(sg_random() * 400));
1118 double speed = sg_random() * sg_random() * 40;
1119 double gust = speed + (10 - sqrt(sg_random() * 100));
1120 Environment::Presets::WindSingleton::instance()->preset(min_hdg, max_hdg, speed, gust);
1121 return FG_OPTIONS_OK;
1125 fgOptWind( const char *arg )
1127 double min_hdg = 0.0, max_hdg = 0.0, speed = 0.0, gust = 0.0;
1128 if (!parse_wind( arg, &min_hdg, &max_hdg, &speed, &gust)) {
1129 SG_LOG( SG_GENERAL, SG_ALERT, "bad wind value " << arg );
1130 return FG_OPTIONS_ERROR;
1132 Environment::Presets::WindSingleton::instance()->preset(min_hdg, max_hdg, speed, gust);
1133 return FG_OPTIONS_OK;
1137 fgOptTurbulence( const char *arg )
1139 Environment::Presets::TurbulenceSingleton::instance()->preset( atof(arg) );
1140 return FG_OPTIONS_OK;
1144 fgOptCeiling( const char *arg )
1146 double elevation, thickness;
1148 string::size_type pos = spec.find(':');
1149 if (pos == string::npos) {
1150 elevation = atof(spec.c_str());
1153 elevation = atof(spec.substr(0, pos).c_str());
1154 thickness = atof(spec.substr(pos + 1).c_str());
1156 Environment::Presets::CeilingSingleton::instance()->preset( elevation, thickness );
1157 return FG_OPTIONS_OK;
1161 fgOptWp( const char *arg )
1163 string_list *waypoints = globals->get_initial_waypoints();
1165 waypoints = new string_list;
1166 globals->set_initial_waypoints(waypoints);
1168 waypoints->push_back(arg);
1169 return FG_OPTIONS_OK;
1173 fgOptConfig( const char *arg )
1177 readProperties(file, globals->get_props());
1178 } catch (const sg_exception &e) {
1179 string message = "Error loading config file: ";
1180 message += e.getFormattedMessage() + e.getOrigin();
1181 SG_LOG(SG_INPUT, SG_ALERT, message);
1182 return FG_OPTIONS_ERROR;
1184 return FG_OPTIONS_OK;
1188 parse_colon (const string &s, double * val1, double * val2)
1190 string::size_type pos = s.find(':');
1191 if (pos == string::npos) {
1195 *val1 = atof(s.substr(0, pos).c_str());
1196 *val2 = atof(s.substr(pos+1).c_str());
1203 fgOptFailure( const char * arg )
1207 fgSetBool("/systems/pitot/serviceable", false);
1208 } else if (a == "static") {
1209 fgSetBool("/systems/static/serviceable", false);
1210 } else if (a == "vacuum") {
1211 fgSetBool("/systems/vacuum/serviceable", false);
1212 } else if (a == "electrical") {
1213 fgSetBool("/systems/electrical/serviceable", false);
1215 SG_LOG(SG_INPUT, SG_ALERT, "Unknown failure mode: " << a);
1216 return FG_OPTIONS_ERROR;
1219 return FG_OPTIONS_OK;
1224 fgOptNAV1( const char * arg )
1226 double radial, freq;
1227 if (parse_colon(arg, &radial, &freq))
1228 fgSetDouble("/instrumentation/nav[0]/radials/selected-deg", radial);
1229 fgSetDouble("/instrumentation/nav[0]/frequencies/selected-mhz", freq);
1230 return FG_OPTIONS_OK;
1234 fgOptNAV2( const char * arg )
1236 double radial, freq;
1237 if (parse_colon(arg, &radial, &freq))
1238 fgSetDouble("/instrumentation/nav[1]/radials/selected-deg", radial);
1239 fgSetDouble("/instrumentation/nav[1]/frequencies/selected-mhz", freq);
1240 return FG_OPTIONS_OK;
1244 fgOptADF1( const char * arg )
1247 if (parse_colon(arg, &rot, &freq))
1248 fgSetDouble("/instrumentation/adf[0]/rotation-deg", rot);
1249 fgSetDouble("/instrumentation/adf[0]/frequencies/selected-khz", freq);
1250 return FG_OPTIONS_OK;
1254 fgOptADF2( const char * arg )
1257 if (parse_colon(arg, &rot, &freq))
1258 fgSetDouble("/instrumentation/adf[1]/rotation-deg", rot);
1259 fgSetDouble("/instrumentation/adf[1]/frequencies/selected-khz", freq);
1260 return FG_OPTIONS_OK;
1264 fgOptDME( const char *arg )
1267 if (opt == "nav1") {
1268 fgSetInt("/instrumentation/dme/switch-position", 1);
1269 fgSetString("/instrumentation/dme/frequencies/source",
1270 "/instrumentation/nav[0]/frequencies/selected-mhz");
1271 } else if (opt == "nav2") {
1272 fgSetInt("/instrumentation/dme/switch-position", 3);
1273 fgSetString("/instrumentation/dme/frequencies/source",
1274 "/instrumentation/nav[1]/frequencies/selected-mhz");
1276 double frequency = atof(arg);
1279 SG_LOG(SG_INPUT, SG_ALERT, "Invalid DME frequency: '" << arg << "'.");
1280 return FG_OPTIONS_ERROR;
1282 fgSetInt("/instrumentation/dme/switch-position", 2);
1283 fgSetString("/instrumentation/dme/frequencies/source",
1284 "/instrumentation/dme/frequencies/selected-mhz");
1285 fgSetDouble("/instrumentation/dme/frequencies/selected-mhz", frequency);
1287 return FG_OPTIONS_OK;
1291 fgOptLivery( const char *arg )
1294 string livery_path = "livery/" + opt;
1295 fgSetString("/sim/model/texture-path", livery_path.c_str() );
1296 return FG_OPTIONS_OK;
1300 fgOptScenario( const char *arg )
1302 SGPropertyNode_ptr ai_node = fgGetNode( "/sim/ai", true );
1303 vector<SGPropertyNode_ptr> scenarii = ai_node->getChildren( "scenario" );
1305 for ( size_t i = 0; i < scenarii.size(); ++i ) {
1306 int ind = scenarii[i]->getIndex();
1307 if ( index < ind ) {
1311 SGPropertyNode_ptr scenario = ai_node->getNode( "scenario", index + 1, true );
1312 scenario->setStringValue( arg );
1313 return FG_OPTIONS_OK;
1317 fgOptRunway( const char *arg )
1319 fgSetString("/sim/presets/runway", arg );
1320 fgSetBool("/sim/presets/runway-requested", true );
1321 return FG_OPTIONS_OK;
1325 fgOptParking( const char *arg )
1327 cerr << "Processing argument " << arg << endl;
1328 fgSetString("/sim/presets/parking", arg );
1329 fgSetBool ("/sim/presets/parking-requested", true );
1330 return FG_OPTIONS_OK;
1334 fgOptVersion( const char *arg )
1336 cerr << "FlightGear version: " << FLIGHTGEAR_VERSION << endl;
1337 cerr << "Revision: " << REVISION << endl;
1338 cerr << "Build-Id: " << HUDSON_BUILD_ID << endl;
1339 cerr << "FG_ROOT=" << globals->get_fg_root() << endl;
1340 cerr << "FG_HOME=" << globals->get_fg_home() << endl;
1341 cerr << "FG_SCENERY=";
1344 PathList scn = globals->get_fg_scenery();
1345 for (PathList::const_iterator it = scn.begin(); it != scn.end(); it++)
1347 if (didsome) cerr << ":";
1352 cerr << "SimGear version: " << SG_STRINGIZE(SIMGEAR_VERSION) << endl;
1353 cerr << "OSG version: " << osgGetVersion() << endl;
1354 cerr << "PLIB version: " << PLIB_VERSION << endl;
1355 return FG_OPTIONS_EXIT;
1359 fgOptCallSign(const char * arg)
1363 strncpy(callsign,arg,10);
1365 for (i=0;callsign[i];i++)
1367 char c = callsign[i];
1368 if (c >= 'A' && c <= 'Z') continue;
1369 if (c >= 'a' && c <= 'z') continue;
1370 if (c >= '0' && c <= '9') continue;
1371 if (c == '-' || c == '_') continue;
1372 // convert any other illegal characters
1375 fgSetString("sim/multiplay/callsign", callsign );
1376 return FG_OPTIONS_OK;
1380 fgOptIgnoreAutosave(const char* arg)
1382 fgSetBool("/sim/startup/ignore-autosave", true);
1383 // don't overwrite autosave on exit
1384 fgSetBool("/sim/startup/save-on-exit", false);
1385 return FG_OPTIONS_OK;
1389 fgOptEnableFreeze(const char* arg)
1391 fgSetBool("/sim/freeze/master", true);
1392 fgSetBool("/sim/freeze/clock", true);
1393 return FG_OPTIONS_OK;
1397 fgOptDisableFreeze(const char* arg)
1399 fgSetBool("/sim/freeze/master", false);
1400 fgSetBool("/sim/freeze/clock", false);
1401 return FG_OPTIONS_OK;
1404 // Set a property for the --prop: option. Syntax: --prop:[<type>:]<name>=<value>
1405 // <type> can be "double" etc. but also only the first letter "d".
1406 // Examples: --prop:alpha=1 --prop:bool:beta=true --prop:d:gamma=0.123
1408 fgOptSetProperty(const char* raw)
1411 string::size_type pos = arg.find('=');
1412 if (pos == arg.npos || pos == 0 || pos + 1 == arg.size())
1413 return FG_OPTIONS_ERROR;
1415 string name = arg.substr(0, pos);
1416 string value = arg.substr(pos + 1);
1418 pos = name.find(':');
1420 if (pos != name.npos && pos != 0 && pos + 1 != name.size()) {
1421 type = name.substr(0, pos);
1422 name = name.substr(pos + 1);
1424 SGPropertyNode *n = fgGetNode(name.c_str(), true);
1426 bool writable = n->getAttribute(SGPropertyNode::WRITE);
1428 n->setAttribute(SGPropertyNode::WRITE, true);
1432 ret = n->setUnspecifiedValue(value.c_str());
1433 else if (type == "s" || type == "string")
1434 ret = n->setStringValue(value.c_str());
1435 else if (type == "d" || type == "double")
1436 ret = n->setDoubleValue(strtod(value.c_str(), 0));
1437 else if (type == "f" || type == "float")
1438 ret = n->setFloatValue(atof(value.c_str()));
1439 else if (type == "l" || type == "long")
1440 ret = n->setLongValue(strtol(value.c_str(), 0, 0));
1441 else if (type == "i" || type == "int")
1442 ret = n->setIntValue(atoi(value.c_str()));
1443 else if (type == "b" || type == "bool")
1444 ret = n->setBoolValue(value == "true" || atoi(value.c_str()) != 0);
1447 n->setAttribute(SGPropertyNode::WRITE, false);
1448 return ret ? FG_OPTIONS_OK : FG_OPTIONS_ERROR;
1452 fgOptLoadTape(const char* arg)
1454 // load a flight recorder tape but wait until the fdm is initialized
1455 class DelayedTapeLoader : SGPropertyChangeListener {
1457 DelayedTapeLoader( const char * tape ) :
1458 _tape(SGPath::fromLocal8Bit(tape))
1460 SGPropertyNode_ptr n = fgGetNode("/sim/signals/fdm-initialized", true);
1461 n->addChangeListener( this );
1464 virtual ~ DelayedTapeLoader() {}
1466 virtual void valueChanged(SGPropertyNode * node)
1468 node->removeChangeListener( this );
1470 // tell the replay subsystem to load the tape
1471 FGReplay* replay = (FGReplay*) globals->get_subsystem("replay");
1472 SGPropertyNode_ptr arg = new SGPropertyNode();
1473 arg->setStringValue("tape", _tape.utf8Str() );
1474 arg->setBoolValue( "same-aircraft", 0 );
1475 replay->loadTape(arg);
1477 delete this; // commence suicide
1484 new DelayedTapeLoader(arg);
1485 return FG_OPTIONS_OK;
1491 option has_param type property b_param s_param func
1494 option : name of the option
1495 has_param : option is --name=value if true or --name if false
1496 type : OPTION_BOOL - property is a boolean
1497 OPTION_STRING - property is a string
1498 OPTION_DOUBLE - property is a double
1499 OPTION_INT - property is an integer
1500 OPTION_CHANNEL - name of option is the name of a channel
1501 OPTION_FUNC - the option trigger a function
1502 b_param : if type==OPTION_BOOL,
1503 value set to the property (has_param is false for boolean)
1504 s_param : if type==OPTION_STRING,
1505 value set to the property if has_param is false
1506 func : function called if type==OPTION_FUNC. if has_param is true,
1507 the value is passed to the function as a string, otherwise,
1510 For OPTION_DOUBLE and OPTION_INT, the parameter value is converted into a
1511 double or an integer and set to the property.
1513 For OPTION_CHANNEL, add_channel is called with the parameter value as the
1517 enum OptionType { OPTION_BOOL = 0, OPTION_STRING, OPTION_DOUBLE, OPTION_INT, OPTION_CHANNEL, OPTION_FUNC, OPTION_IGNORE };
1518 const int OPTION_MULTI = 1 << 17;
1524 const char *property;
1526 const char *s_param;
1527 int (*func)( const char * );
1528 } fgOptionArray[] = {
1530 {"language", true, OPTION_IGNORE, "", false, "", 0 },
1531 {"console", false, OPTION_IGNORE, "", false, "", 0 },
1532 {"launcher", false, OPTION_IGNORE, "", false, "", 0 },
1533 {"disable-rembrandt", false, OPTION_BOOL, "/sim/rendering/rembrandt/enabled", false, "", 0 },
1534 {"enable-rembrandt", false, OPTION_BOOL, "/sim/rendering/rembrandt/enabled", true, "", 0 },
1535 {"renderer", true, OPTION_STRING, "/sim/rendering/rembrandt/renderer", false, "", 0 },
1536 {"disable-splash-screen", false, OPTION_BOOL, "/sim/startup/splash-screen", false, "", 0 },
1537 {"enable-splash-screen", false, OPTION_BOOL, "/sim/startup/splash-screen", true, "", 0 },
1538 {"disable-mouse-pointer", false, OPTION_STRING, "/sim/startup/mouse-pointer", false, "disabled", 0 },
1539 {"enable-mouse-pointer", false, OPTION_STRING, "/sim/startup/mouse-pointer", false, "enabled", 0 },
1540 {"disable-random-objects", false, OPTION_BOOL, "/sim/rendering/random-objects", false, "", 0 },
1541 {"enable-random-objects", false, OPTION_BOOL, "/sim/rendering/random-objects", true, "", 0 },
1542 {"disable-random-vegetation", false, OPTION_BOOL, "/sim/rendering/random-vegetation", false, "", 0 },
1543 {"enable-random-vegetation", false, OPTION_BOOL, "/sim/rendering/random-vegetation", true, "", 0 },
1544 {"disable-random-buildings", false, OPTION_BOOL, "/sim/rendering/random-buildings", false, "", 0 },
1545 {"enable-random-buildings", false, OPTION_BOOL, "/sim/rendering/random-buildings", true, "", 0 },
1546 {"disable-real-weather-fetch", false, OPTION_BOOL, "/environment/realwx/enabled", false, "", 0 },
1547 {"enable-real-weather-fetch", false, OPTION_BOOL, "/environment/realwx/enabled", true, "", 0 },
1548 {"metar", true, OPTION_FUNC, "", false, "", fgOptMetar },
1549 {"disable-ai-models", false, OPTION_BOOL, "/sim/ai/enabled", false, "", 0 },
1550 {"enable-ai-models", false, OPTION_BOOL, "/sim/ai/enabled", true, "", 0 },
1551 {"disable-ai-traffic", false, OPTION_BOOL, "/sim/traffic-manager/enabled", false, "", 0 },
1552 {"enable-ai-traffic", false, OPTION_BOOL, "/sim/traffic-manager/enabled", true, "", 0 },
1553 {"disable-freeze", false, OPTION_FUNC, "", false, "", fgOptDisableFreeze },
1554 {"enable-freeze", false, OPTION_FUNC, "", true, "", fgOptEnableFreeze },
1555 {"disable-fuel-freeze", false, OPTION_BOOL, "/sim/freeze/fuel", false, "", 0 },
1556 {"enable-fuel-freeze", false, OPTION_BOOL, "/sim/freeze/fuel", true, "", 0 },
1557 {"disable-clock-freeze", false, OPTION_BOOL, "/sim/freeze/clock", false, "", 0 },
1558 {"enable-clock-freeze", false, OPTION_BOOL, "/sim/freeze/clock", true, "", 0 },
1559 {"disable-hud-3d", false, OPTION_BOOL, "/sim/hud/enable3d[1]", false, "", 0 },
1560 {"enable-hud-3d", false, OPTION_BOOL, "/sim/hud/enable3d[1]", true, "", 0 },
1561 {"disable-anti-alias-hud", false, OPTION_BOOL, "/sim/hud/color/antialiased", false, "", 0 },
1562 {"enable-anti-alias-hud", false, OPTION_BOOL, "/sim/hud/color/antialiased", true, "", 0 },
1563 {"disable-auto-coordination", false, OPTION_BOOL, "/controls/flight/auto-coordination", false, "", 0 },
1564 {"enable-auto-coordination", false, OPTION_BOOL, "/controls/flight/auto-coordination", true, "", 0 },
1565 {"browser-app", true, OPTION_STRING, "/sim/startup/browser-app", false, "", 0 },
1566 {"disable-hud", false, OPTION_BOOL, "/sim/hud/visibility[1]", false, "", 0 },
1567 {"enable-hud", false, OPTION_BOOL, "/sim/hud/visibility[1]", true, "", 0 },
1568 {"disable-panel", false, OPTION_BOOL, "/sim/panel/visibility", false, "", 0 },
1569 {"enable-panel", false, OPTION_BOOL, "/sim/panel/visibility", true, "", 0 },
1570 {"disable-sound", false, OPTION_BOOL, "/sim/sound/working", false, "", 0 },
1571 {"enable-sound", false, OPTION_BOOL, "/sim/sound/working", true, "", 0 },
1572 {"sound-device", true, OPTION_STRING, "/sim/sound/device-name", false, "", 0 },
1573 {"airport", true, OPTION_STRING, "/sim/presets/airport-id", false, "", 0 },
1574 {"runway", true, OPTION_FUNC, "", false, "", fgOptRunway },
1575 {"vor", true, OPTION_FUNC, "", false, "", fgOptVOR },
1576 {"vor-frequency", true, OPTION_DOUBLE, "/sim/presets/vor-freq", false, "", fgOptVOR },
1577 {"ndb", true, OPTION_FUNC, "", false, "", fgOptNDB },
1578 {"ndb-frequency", true, OPTION_DOUBLE, "/sim/presets/ndb-freq", false, "", fgOptVOR },
1579 {"carrier", true, OPTION_FUNC, "", false, "", fgOptCarrier },
1580 {"parkpos", true, OPTION_FUNC, "", false, "", fgOptParkpos },
1581 {"fix", true, OPTION_FUNC, "", false, "", fgOptFIX },
1582 {"offset-distance", true, OPTION_DOUBLE, "/sim/presets/offset-distance-nm", false, "", 0 },
1583 {"offset-azimuth", true, OPTION_DOUBLE, "/sim/presets/offset-azimuth-deg", false, "", 0 },
1584 {"lon", true, OPTION_FUNC, "", false, "", fgOptLon },
1585 {"lat", true, OPTION_FUNC, "", false, "", fgOptLat },
1586 {"altitude", true, OPTION_FUNC, "", false, "", fgOptAltitude },
1587 {"uBody", true, OPTION_FUNC, "", false, "", fgOptUBody },
1588 {"vBody", true, OPTION_FUNC, "", false, "", fgOptVBody },
1589 {"wBody", true, OPTION_FUNC, "", false, "", fgOptWBody },
1590 {"vNorth", true, OPTION_FUNC, "", false, "", fgOptVNorth },
1591 {"vEast", true, OPTION_FUNC, "", false, "", fgOptVEast },
1592 {"vDown", true, OPTION_FUNC, "", false, "", fgOptVDown },
1593 {"vc", true, OPTION_FUNC, "", false, "", fgOptVc },
1594 {"mach", true, OPTION_FUNC, "", false, "", fgOptMach },
1595 {"heading", true, OPTION_DOUBLE, "/sim/presets/heading-deg", false, "", 0 },
1596 {"roll", true, OPTION_DOUBLE, "/sim/presets/roll-deg", false, "", 0 },
1597 {"pitch", true, OPTION_DOUBLE, "/sim/presets/pitch-deg", false, "", 0 },
1598 {"glideslope", true, OPTION_DOUBLE, "/sim/presets/glideslope-deg", false, "", 0 },
1599 {"roc", true, OPTION_FUNC, "", false, "", fgOptRoc },
1600 {"fg-root", true, OPTION_IGNORE, "", false, "", 0 },
1601 {"fg-scenery", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptFgScenery },
1602 {"fg-aircraft", true, OPTION_IGNORE | OPTION_MULTI, "", false, "", 0 },
1603 {"fdm", true, OPTION_STRING, "/sim/flight-model", false, "", 0 },
1604 {"aero", true, OPTION_STRING, "/sim/aero", false, "", 0 },
1605 {"aircraft-dir", true, OPTION_IGNORE, "", false, "", 0 },
1606 {"state", true, OPTION_IGNORE, "", false, "", 0 },
1607 {"model-hz", true, OPTION_INT, "/sim/model-hz", false, "", 0 },
1608 {"max-fps", true, OPTION_DOUBLE, "/sim/frame-rate-throttle-hz", false, "", 0 },
1609 {"speed", true, OPTION_DOUBLE, "/sim/speed-up", false, "", 0 },
1610 {"trim", false, OPTION_BOOL, "/sim/presets/trim", true, "", 0 },
1611 {"notrim", false, OPTION_BOOL, "/sim/presets/trim", false, "", 0 },
1612 {"on-ground", false, OPTION_BOOL, "/sim/presets/onground", true, "", 0 },
1613 {"in-air", false, OPTION_BOOL, "/sim/presets/onground", false, "", 0 },
1614 {"fog-disable", false, OPTION_STRING, "/sim/rendering/fog", false, "disabled", 0 },
1615 {"fog-fastest", false, OPTION_STRING, "/sim/rendering/fog", false, "fastest", 0 },
1616 {"fog-nicest", false, OPTION_STRING, "/sim/rendering/fog", false, "nicest", 0 },
1617 {"disable-horizon-effect", false, OPTION_BOOL, "/sim/rendering/horizon-effect", false, "", 0 },
1618 {"enable-horizon-effect", false, OPTION_BOOL, "/sim/rendering/horizon-effect", true, "", 0 },
1619 {"disable-enhanced-lighting", false, OPTION_BOOL, "/sim/rendering/enhanced-lighting", false, "", 0 },
1620 {"enable-enhanced-lighting", false, OPTION_BOOL, "/sim/rendering/enhanced-lighting", true, "", 0 },
1621 {"disable-distance-attenuation", false, OPTION_BOOL, "/sim/rendering/distance-attenuation", false, "", 0 },
1622 {"enable-distance-attenuation", false, OPTION_BOOL, "/sim/rendering/distance-attenuation", true, "", 0 },
1623 {"disable-specular-highlight", false, OPTION_BOOL, "/sim/rendering/specular-highlight", false, "", 0 },
1624 {"enable-specular-highlight", false, OPTION_BOOL, "/sim/rendering/specular-highlight", true, "", 0 },
1625 {"disable-clouds", false, OPTION_BOOL, "/environment/clouds/status", false, "", 0 },
1626 {"enable-clouds", false, OPTION_BOOL, "/environment/clouds/status", true, "", 0 },
1627 {"disable-clouds3d", false, OPTION_BOOL, "/sim/rendering/clouds3d-enable", false, "", 0 },
1628 {"enable-clouds3d", false, OPTION_BOOL, "/sim/rendering/clouds3d-enable", true, "", 0 },
1629 {"fov", true, OPTION_FUNC, "", false, "", fgOptFov },
1630 {"aspect-ratio-multiplier", true, OPTION_DOUBLE, "/sim/current-view/aspect-ratio-multiplier", false, "", 0 },
1631 {"disable-fullscreen", false, OPTION_BOOL, "/sim/startup/fullscreen", false, "", 0 },
1632 {"enable-fullscreen", false, OPTION_BOOL, "/sim/startup/fullscreen", true, "", 0 },
1633 {"disable-save-on-exit", false, OPTION_BOOL, "/sim/startup/save-on-exit", false, "", 0 },
1634 {"enable-save-on-exit", false, OPTION_BOOL, "/sim/startup/save-on-exit", true, "", 0 },
1635 {"read-only", false, OPTION_BOOL, "/sim/fghome-readonly", true, "", 0 },
1636 {"ignore-autosave", false, OPTION_FUNC, "", false, "", fgOptIgnoreAutosave },
1637 {"restore-defaults", false, OPTION_BOOL, "/sim/startup/restore-defaults", true, "", 0 },
1638 {"shading-flat", false, OPTION_BOOL, "/sim/rendering/shading", false, "", 0 },
1639 {"shading-smooth", false, OPTION_BOOL, "/sim/rendering/shading", true, "", 0 },
1640 {"texture-filtering", false, OPTION_INT, "/sim/rendering/filtering", 1, "", 0 },
1641 {"disable-wireframe", false, OPTION_BOOL, "/sim/rendering/wireframe", false, "", 0 },
1642 {"enable-wireframe", false, OPTION_BOOL, "/sim/rendering/wireframe", true, "", 0 },
1643 {"materials-file", true, OPTION_STRING, "/sim/rendering/materials-file", false, "", 0 },
1644 {"disable-terrasync", false, OPTION_BOOL, "/sim/terrasync/enabled", false, "", 0 },
1645 {"enable-terrasync", false, OPTION_BOOL, "/sim/terrasync/enabled", true, "", 0 },
1646 {"terrasync-dir", true, OPTION_FUNC, "", false, "", fgOptTerrasyncDir },
1647 {"download-dir", true, OPTION_STRING, "/sim/paths/download-dir", false, "", 0 },
1648 {"geometry", true, OPTION_FUNC, "", false, "", fgOptGeometry },
1649 {"bpp", true, OPTION_FUNC, "", false, "", fgOptBpp },
1650 {"units-feet", false, OPTION_STRING, "/sim/startup/units", false, "feet", 0 },
1651 {"units-meters", false, OPTION_STRING, "/sim/startup/units", false, "meters", 0 },
1652 {"timeofday", true, OPTION_STRING, "/sim/startup/time-offset-type", false, "noon", 0 },
1653 {"season", true, OPTION_STRING, "/sim/startup/season", false, "summer", 0 },
1654 {"time-offset", true, OPTION_FUNC, "", false, "", fgOptTimeOffset },
1655 {"time-match-real", false, OPTION_STRING, "/sim/startup/time-offset-type", false, "system-offset", 0 },
1656 {"time-match-local", false, OPTION_STRING, "/sim/startup/time-offset-type", false, "latitude-offset", 0 },
1657 {"start-date-sys", true, OPTION_FUNC, "", false, "", fgOptStartDateSys },
1658 {"start-date-lat", true, OPTION_FUNC, "", false, "", fgOptStartDateLat },
1659 {"start-date-gmt", true, OPTION_FUNC, "", false, "", fgOptStartDateGmt },
1660 {"hud-tris", false, OPTION_STRING, "/sim/hud/frame-stat-type", false, "tris", 0 },
1661 {"hud-culled", false, OPTION_STRING, "/sim/hud/frame-stat-type", false, "culled", 0 },
1662 {"atcsim", true, OPTION_CHANNEL, "", false, "dummy", 0 },
1663 {"atlas", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
1664 {"httpd", true, OPTION_FUNC , "", false, "", fgOptHttpd },
1665 {"jpg-httpd", true, OPTION_FUNC, "", false, "", fgOptJpgHttpd },
1666 {"native", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
1667 {"native-ctrls", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
1668 {"native-fdm", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
1669 {"native-gui", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
1670 {"opengc", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
1671 {"AV400", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
1672 {"AV400Sim", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
1673 {"AV400WSimA", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
1674 {"AV400WSimB", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
1675 {"garmin", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
1676 {"igc", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
1677 {"nmea", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
1678 {"generic", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
1679 {"props", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
1680 {"telnet", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
1681 {"pve", true, OPTION_CHANNEL, "", false, "", 0 },
1682 {"ray", true, OPTION_CHANNEL, "", false, "", 0 },
1683 {"rul", true, OPTION_CHANNEL, "", false, "", 0 },
1684 {"joyclient", true, OPTION_CHANNEL, "", false, "", 0 },
1685 {"jsclient", true, OPTION_CHANNEL, "", false, "", 0 },
1686 {"proxy", true, OPTION_FUNC, "", false, "", fgSetupProxy },
1687 {"callsign", true, OPTION_FUNC, "", false, "", fgOptCallSign},
1688 {"multiplay", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
1690 {"hla", true, OPTION_CHANNEL, "", false, "", 0 },
1691 {"hla-local", true, OPTION_CHANNEL, "", false, "", 0 },
1693 {"trace-read", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptTraceRead },
1694 {"trace-write", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptTraceWrite },
1695 {"log-level", true, OPTION_FUNC, "", false, "", fgOptLogLevel },
1696 {"log-class", true, OPTION_FUNC, "", false, "", fgOptLogClasses },
1697 {"view-offset", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptViewOffset },
1698 {"visibility", true, OPTION_FUNC, "", false, "", fgOptVisibilityMeters },
1699 {"visibility-miles", true, OPTION_FUNC, "", false, "", fgOptVisibilityMiles },
1700 {"random-wind", false, OPTION_FUNC, "", false, "", fgOptRandomWind },
1701 {"wind", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptWind },
1702 {"turbulence", true, OPTION_FUNC, "", false, "", fgOptTurbulence },
1703 {"ceiling", true, OPTION_FUNC, "", false, "", fgOptCeiling },
1704 {"wp", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptWp },
1705 {"flight-plan", true, OPTION_STRING, "/autopilot/route-manager/file-path", false, "", NULL },
1706 {"config", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptConfig },
1707 {"aircraft", true, OPTION_STRING, "/sim/aircraft", false, "", 0 },
1708 {"vehicle", true, OPTION_STRING, "/sim/aircraft", false, "", 0 },
1709 {"failure", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptFailure },
1711 {"enable-fgcom", false, OPTION_BOOL, "/sim/fgcom/enabled", true, "", 0 },
1712 {"disable-fgcom", false, OPTION_BOOL, "/sim/fgcom/enabled", false, "", 0 },
1714 {"com1", true, OPTION_DOUBLE, "/instrumentation/comm[0]/frequencies/selected-mhz", false, "", 0 },
1715 {"com2", true, OPTION_DOUBLE, "/instrumentation/comm[1]/frequencies/selected-mhz", false, "", 0 },
1716 {"nav1", true, OPTION_FUNC, "", false, "", fgOptNAV1 },
1717 {"nav2", true, OPTION_FUNC, "", false, "", fgOptNAV2 },
1718 {"adf", /*legacy*/ true, OPTION_FUNC, "", false, "", fgOptADF1 },
1719 {"adf1", true, OPTION_FUNC, "", false, "", fgOptADF1 },
1720 {"adf2", true, OPTION_FUNC, "", false, "", fgOptADF2 },
1721 {"dme", true, OPTION_FUNC, "", false, "", fgOptDME },
1722 {"min-status", true, OPTION_STRING, "/sim/aircraft-min-status", false, "all", 0 },
1723 {"livery", true, OPTION_FUNC, "", false, "", fgOptLivery },
1724 {"ai-scenario", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptScenario },
1725 {"parking-id", true, OPTION_FUNC, "", false, "", fgOptParking },
1726 {"version", false, OPTION_FUNC, "", false, "", fgOptVersion },
1727 {"enable-fpe", false, OPTION_IGNORE, "", false, "", 0},
1728 {"fgviewer", false, OPTION_IGNORE, "", false, "", 0},
1729 {"no-default-config", false, OPTION_IGNORE, "", false, "", 0},
1730 {"prop", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptSetProperty},
1731 {"load-tape", true, OPTION_FUNC, "", false, "", fgOptLoadTape },
1736 namespace flightgear
1740 * internal storage of a value->option binding
1745 OptionValue(OptionDesc* d, const string& v) :
1753 typedef std::vector<OptionValue> OptionValueVec;
1754 typedef std::map<string, OptionDesc*> OptionDescDict;
1756 class Options::OptionsPrivate
1760 OptionValueVec::const_iterator findValue(const string& key) const
1762 OptionValueVec::const_iterator it = values.begin();
1763 for (; it != values.end(); ++it) {
1765 continue; // ignore markers
1768 if (it->desc->option == key) {
1771 } // of set values iteration
1773 return it; // not found
1776 OptionValueVec::iterator findValue(const string& key)
1778 OptionValueVec::iterator it = values.begin();
1779 for (; it != values.end(); ++it) {
1781 continue; // ignore markers
1784 if (it->desc->option == key) {
1787 } // of set values iteration
1789 return it; // not found
1792 OptionDesc* findOption(const string& key) const
1794 OptionDescDict::const_iterator it = options.find(key);
1795 if (it == options.end()) {
1802 int processOption(OptionDesc* desc, const string& arg_value)
1805 return FG_OPTIONS_OK; // tolerate marker options
1808 switch ( desc->type & 0xffff ) {
1810 fgSetBool( desc->property, desc->b_param );
1813 if ( desc->has_param && !arg_value.empty() ) {
1814 fgSetString( desc->property, arg_value.c_str() );
1815 } else if ( !desc->has_param && arg_value.empty() ) {
1816 fgSetString( desc->property, desc->s_param );
1817 } else if ( desc->has_param ) {
1818 SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
1819 return FG_OPTIONS_ERROR;
1821 SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' does not have a parameter" );
1822 return FG_OPTIONS_ERROR;
1826 if ( !arg_value.empty() ) {
1827 fgSetDouble( desc->property, atof( arg_value ) );
1829 SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
1830 return FG_OPTIONS_ERROR;
1834 if ( !arg_value.empty() ) {
1835 fgSetInt( desc->property, atoi( arg_value ) );
1837 SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
1838 return FG_OPTIONS_ERROR;
1841 case OPTION_CHANNEL:
1842 // XXX return value of add_channel should be checked?
1843 if ( desc->has_param && !arg_value.empty() ) {
1844 add_channel( desc->option, arg_value );
1845 } else if ( !desc->has_param && arg_value.empty() ) {
1846 add_channel( desc->option, desc->s_param );
1847 } else if ( desc->has_param ) {
1848 SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
1849 return FG_OPTIONS_ERROR;
1851 SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' does not have a parameter" );
1852 return FG_OPTIONS_ERROR;
1856 if ( desc->has_param && !arg_value.empty() ) {
1857 return desc->func( arg_value.c_str() );
1858 } else if ( !desc->has_param && arg_value.empty() ) {
1859 return desc->func( desc->s_param );
1860 } else if ( desc->has_param ) {
1861 SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
1862 return FG_OPTIONS_ERROR;
1864 SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' does not have a parameter" );
1865 return FG_OPTIONS_ERROR;
1873 return FG_OPTIONS_OK;
1877 * insert a marker value into the values vector. This is necessary
1878 * when processing options, to ensure the correct ordering, where we scan
1879 * for marker values in reverse, and then forwards within each group.
1881 void insertGroupMarker()
1883 values.push_back(OptionValue(NULL, "-"));
1887 * given a current iterator into the values, find the preceding group marker,
1888 * or return the beginning of the value vector.
1890 OptionValueVec::const_iterator rfindGroup(OptionValueVec::const_iterator pos) const
1892 while (--pos != values.begin()) {
1893 if (pos->desc == NULL) {
1894 return pos; // found a marker, we're done
1904 shouldLoadDefaultConfig;
1906 OptionDescDict options;
1907 OptionValueVec values;
1908 simgear::PathList propertyFiles;
1911 Options* Options::sharedInstance()
1913 if (shared_instance == NULL) {
1914 shared_instance = new Options;
1917 return shared_instance;
1920 Options::Options() :
1921 p(new OptionsPrivate())
1923 p->showHelp = false;
1925 p->showAircraft = false;
1926 p->shouldLoadDefaultConfig = true;
1929 OptionDesc *desc = &fgOptionArray[ 0 ];
1930 while ( desc->option != 0 ) {
1931 p->options[ desc->option ] = desc;
1940 void Options::init(int argc, char **argv, const SGPath& appDataPath)
1942 // first, process the command line
1943 bool inOptions = true;
1944 for (int i=1; i<argc; ++i) {
1945 if (inOptions && (argv[i][0] == '-')) {
1946 if (strcmp(argv[i], "--") == 0) { // end of options delimiter
1951 int result = parseOption(argv[i]);
1952 processArgResult(result);
1954 // XML properties file
1955 SGPath f = SGPath::fromLocal8Bit(argv[i]);
1957 SG_LOG(SG_GENERAL, SG_ALERT, "config file not found:" << f);
1959 p->propertyFiles.push_back(f);
1962 } // of arguments iteration
1963 p->insertGroupMarker(); // command line is one group
1965 // establish log-level before anything else - otherwise it is not possible
1966 // to show extra (debug/info/warning) messages for the start-up phase.
1967 fgOptLogLevel(valueForOption("log-level", "alert").c_str());
1969 if (!p->shouldLoadDefaultConfig) {
1970 setupRoot(argc, argv);
1974 // then config files
1978 if( !hostname.empty() ) {
1979 // Check for ~/.fgfsrc.hostname
1980 config = SGPath::home();
1981 config.append(".fgfsrc");
1982 config.concat( "." );
1983 config.concat( hostname );
1987 // Check for ~/.fgfsrc
1988 config = SGPath::home();
1989 config.append(".fgfsrc");
1992 // check for a config file in app data
1993 SGPath appDataConfig(appDataPath);
1994 appDataConfig.append("fgfsrc");
1995 if (appDataConfig.exists()) {
1996 readConfig(appDataConfig);
2000 setupRoot(argc, argv);
2002 // system.fgfsrc is disabled, as we no longer allow anything in fgdata to set
2003 // fg-root/fg-home/fg-aircraft and hence control what files Nasal can access
2004 std::string name_for_error = config.utf8Str();
2005 if( ! hostname.empty() ) {
2006 config = globals->get_fg_root();
2007 config.append( "system.fgfsrc" );
2008 config.concat( "." );
2009 config.concat( hostname );
2010 if (config.exists()) {
2011 flightgear::fatalMessageBox("Unsupported configuration",
2012 "You have a " + config.utf8Str() + " file, which is no longer processed for security reasons",
2013 "If you created this file intentionally, please move it to " + name_for_error);
2017 config = globals->get_fg_root();
2018 config.append( "system.fgfsrc" );
2019 if (config.exists()) {
2020 flightgear::fatalMessageBox("Unsupported configuration",
2021 "You have a " + config.utf8Str() + " file, which is no longer processed for security reasons",
2022 "If you created this file intentionally, please move it to " + name_for_error);
2026 void Options::initPaths()
2028 BOOST_FOREACH(const string& pathOpt, valuesForOption("fg-aircraft")) {
2029 PathList paths = SGPath::pathsFromLocal8Bit(pathOpt);
2030 globals->append_aircraft_paths(paths);
2033 const char* envp = ::getenv("FG_AIRCRAFT");
2035 globals->append_aircraft_paths(SGPath::pathsFromEnv("FG_AIRCRAFT"));
2040 void Options::initAircraft()
2043 if (isOptionSet("aircraft")) {
2044 aircraft = valueForOption("aircraft");
2045 } else if (isOptionSet("vehicle")) {
2046 aircraft = valueForOption("vehicle");
2049 if (!aircraft.empty()) {
2050 SG_LOG(SG_INPUT, SG_INFO, "aircraft = " << aircraft );
2051 fgSetString("/sim/aircraft", aircraft.c_str() );
2053 SG_LOG(SG_INPUT, SG_INFO, "No user specified aircraft, using default" );
2056 if (p->showAircraft) {
2059 fgOptLogLevel( "alert" );
2061 // First place to check is the 'Aircraft' sub-directory in $FG_ROOT
2063 SGPath rootAircraft = globals->get_fg_root();
2064 rootAircraft.append("Aircraft");
2065 path_list.push_back(rootAircraft);
2067 // Additionally, aircraft may also be found in user-defined places
2068 // (via $FG_AIRCRAFT or with the '--fg-aircraft' option)
2069 PathList aircraft_paths = globals->get_aircraft_paths();
2071 path_list.insert(path_list.end(), aircraft_paths.begin(),
2072 aircraft_paths.end());
2074 fgShowAircraft(path_list);
2078 if (isOptionSet("aircraft-dir")) {
2079 SGPath aircraftDirPath(valueForOption("aircraft-dir"));
2081 // Set this now, so it's available in FindAndCacheAircraft. Use realpath()
2082 // as in FGGlobals::append_aircraft_path(), otherwise fgValidatePath()
2083 // will deny access to resources under this path if one of its components
2084 // is a symlink (which is not a problem, since it was given as is by the
2085 // user---this is very different from a symlink *under* the aircraft dir
2086 // or a scenery dir).
2087 fgSetString("/sim/aircraft-dir", aircraftDirPath.realpath().utf8Str());
2090 if (isOptionSet("state")) {
2091 std::string stateName = valueForOption("state");
2092 // can't validate this until the -set.xml is parsed
2093 fgSetString("/sim/aircraft-state", stateName);
2097 void Options::processArgResult(int result)
2099 if ((result == FG_OPTIONS_HELP) || (result == FG_OPTIONS_ERROR))
2101 else if (result == FG_OPTIONS_VERBOSE_HELP)
2103 else if (result == FG_OPTIONS_SHOW_AIRCRAFT) {
2104 p->showAircraft = true;
2105 } else if (result == FG_OPTIONS_NO_DEFAULT_CONFIG) {
2106 p->shouldLoadDefaultConfig = false;
2107 } else if (result == FG_OPTIONS_SHOW_SOUND_DEVICES) {
2111 string vendor = smgr.get_vendor();
2112 string renderer = smgr.get_renderer();
2113 cout << renderer << " provided by " << vendor << endl;
2114 cout << endl << "No. Device" << endl;
2116 vector <const char*>devices = smgr.get_available_devices();
2117 for (vector <const char*>::size_type i=0; i<devices.size(); i++) {
2118 cout << i << ". \"" << devices[i] << "\"" << endl;
2123 } else if (result == FG_OPTIONS_EXIT) {
2128 void Options::readConfig(const SGPath& path)
2130 sg_gzifstream in( path );
2131 if ( !in.is_open() ) {
2135 SG_LOG( SG_GENERAL, SG_INFO, "Processing config file: " << path );
2138 while ( ! in.eof() ) {
2140 getline( in, line, '\n' );
2142 // catch extraneous (DOS) line ending character
2144 for (i = line.length(); i > 0; i--)
2145 if (line[i - 1] > 32)
2147 line = line.substr( 0, i );
2149 if ( parseOption( line ) == FG_OPTIONS_ERROR ) {
2150 cerr << endl << "Config file parse error: " << path << " '"
2151 << line << "'" << endl;
2157 p->insertGroupMarker(); // each config file is a group
2160 int Options::parseOption(const string& s)
2162 if ((s == "--help") || (s=="-h")) {
2163 return FG_OPTIONS_HELP;
2164 } else if ( (s == "--verbose") || (s == "-v") ) {
2165 // verbose help/usage request
2166 return FG_OPTIONS_VERBOSE_HELP;
2167 } else if ((s == "--console") || (s == "-c")) {
2168 simgear::requestConsole();
2169 return FG_OPTIONS_OK;
2170 } else if (s.find("-psn") == 0) {
2171 // on Mac, when launched from the GUI, we are passed the ProcessSerialNumber
2172 // as an argument (and no others). Silently ignore the argument here.
2173 return FG_OPTIONS_OK;
2174 } else if ( s.find( "--show-aircraft") == 0) {
2175 return(FG_OPTIONS_SHOW_AIRCRAFT);
2176 } else if ( s.find( "--show-sound-devices") == 0) {
2177 return(FG_OPTIONS_SHOW_SOUND_DEVICES);
2178 } else if ( s.find( "--no-default-config") == 0) {
2179 return FG_OPTIONS_NO_DEFAULT_CONFIG;
2180 } else if ( s.find( "--prop:") == 0) {
2181 // property setting has a slightly different syntax, so fudge things
2182 OptionDesc* desc = p->findOption("prop");
2183 if (s.find("=", 7) == string::npos) { // no equals token
2184 SG_LOG(SG_GENERAL, SG_ALERT, "malformed property option:" << s);
2185 return FG_OPTIONS_ERROR;
2188 p->values.push_back(OptionValue(desc, s.substr(7)));
2189 return FG_OPTIONS_OK;
2190 } else if ( s.find( "--" ) == 0 ) {
2191 size_t eqPos = s.find( '=' );
2193 if (eqPos == string::npos) {
2196 key = s.substr( 2, eqPos - 2 );
2197 value = s.substr( eqPos + 1);
2200 return addOption(key, value);
2202 flightgear::modalMessageBox("Unknown option", "Unknown command-line option: " + s);
2203 return FG_OPTIONS_ERROR;
2207 int Options::addOption(const string &key, const string &value)
2209 OptionDesc* desc = p->findOption(key);
2211 flightgear::modalMessageBox("Unknown option", "Unknown command-line option: " + key);
2212 return FG_OPTIONS_ERROR;
2215 if (!(desc->type & OPTION_MULTI)) {
2216 OptionValueVec::const_iterator it = p->findValue(key);
2217 if (it != p->values.end()) {
2218 SG_LOG(SG_GENERAL, SG_WARN, "multiple values forbidden for option:" << key << ", ignoring:" << value);
2219 return FG_OPTIONS_OK;
2223 p->values.push_back(OptionValue(desc, value));
2224 return FG_OPTIONS_OK;
2227 int Options::setOption(const string &key, const string &value)
2229 OptionDesc* desc = p->findOption(key);
2231 flightgear::modalMessageBox("Unknown option", "Unknown command-line option: " + key);
2232 return FG_OPTIONS_ERROR;
2235 if (!(desc->type & OPTION_MULTI)) {
2236 OptionValueVec::iterator it = p->findValue(key);
2237 if (it != p->values.end()) {
2238 // remove existing valye
2239 p->values.erase(it);
2243 p->values.push_back(OptionValue(desc, value));
2244 return FG_OPTIONS_OK;
2247 void Options::clearOption(const std::string& key)
2249 OptionValueVec::iterator it = p->findValue(key);
2250 for (; it != p->values.end(); it = p->findValue(key)) {
2251 p->values.erase(it);
2255 bool Options::isOptionSet(const string &key) const
2257 OptionValueVec::const_iterator it = p->findValue(key);
2258 return (it != p->values.end());
2261 string Options::valueForOption(const string& key, const string& defValue) const
2263 OptionValueVec::const_iterator it = p->findValue(key);
2264 if (it == p->values.end()) {
2271 string_list Options::valuesForOption(const std::string& key) const
2274 OptionValueVec::const_iterator it = p->values.begin();
2275 for (; it != p->values.end(); ++it) {
2277 continue; // ignore marker values
2280 if (it->desc->option == key) {
2281 result.push_back(it->value);
2288 SGPath defaultDownloadDir()
2290 #if defined(SG_WINDOWS)
2291 SGPath p(SGPath::documents());
2292 p.append("FlightGear");
2294 SGPath p(globals->get_fg_home());
2299 OptionResult Options::processOptions()
2301 // establish locale before showing help (this selects the default locale,
2302 // when no explicit option was set)
2303 globals->get_locale()->selectLanguage(valueForOption("language").c_str());
2305 // now FG_ROOT is setup, process various command line options that bail us
2306 // out quickly, but rely on aircraft / root settings
2309 return FG_OPTIONS_EXIT;
2312 // processing order is complicated. We must process groups LIFO, but the
2313 // values *within* each group in FIFO order, to retain consistency with
2314 // older versions of FG, and existing user configs.
2315 // in practice this means system.fgfsrc must be *processed* before
2316 // .fgfsrc, which must be processed before the command line args, and so on.
2317 OptionValueVec::const_iterator groupEnd = p->values.end();
2319 while (groupEnd != p->values.begin()) {
2320 OptionValueVec::const_iterator groupBegin = p->rfindGroup(groupEnd);
2321 // run over the group in FIFO order
2322 OptionValueVec::const_iterator it;
2323 for (it = groupBegin; it != groupEnd; ++it) {
2324 int result = p->processOption(it->desc, it->value);
2327 case FG_OPTIONS_ERROR:
2329 return FG_OPTIONS_ERROR;
2331 case FG_OPTIONS_EXIT:
2332 return FG_OPTIONS_EXIT;
2338 SG_LOG(SG_GENERAL, SG_INFO, "\toption:" << it->desc->option << " = " << it->value);
2342 groupEnd = groupBegin;
2345 BOOST_FOREACH(const SGPath& file, p->propertyFiles) {
2346 SG_LOG(SG_GENERAL, SG_INFO,
2347 "Reading command-line property file " << file);
2348 readProperties(file, globals->get_props());
2351 // now options are process, do supplemental fixup
2352 const char *envp = ::getenv( "FG_SCENERY" );
2354 globals->append_fg_scenery(SGPath::pathsFromEnv("FG_SCENERY"), true);
2357 // download dir fix-up
2358 SGPath downloadDir = SGPath::fromUtf8(fgGetString("/sim/paths/download-dir"));
2359 if (downloadDir.isNull()) {
2360 downloadDir = defaultDownloadDir();
2361 SG_LOG(SG_GENERAL, SG_INFO, "Using default download dir: " << downloadDir);
2363 simgear::Dir d(downloadDir);
2365 SG_LOG(SG_GENERAL, SG_INFO, "Creating requested download dir: " << downloadDir);
2370 // terrasync directory fixup
2371 SGPath terrasyncDir = SGPath::fromUtf8(fgGetString("/sim/terrasync/scenery-dir"));
2372 if (terrasyncDir.isNull()) {
2373 SGPath p(downloadDir);
2374 p.append("TerraSync");
2377 simgear::Dir d(terrasyncDir);
2382 SG_LOG(SG_GENERAL, SG_INFO, "Using default TerraSync: " << terrasyncDir);
2383 fgSetString("/sim/terrasync/scenery-dir", p.utf8Str());
2385 SG_LOG(SG_GENERAL, SG_INFO, "Using explicit TerraSync dir: " << terrasyncDir);
2388 // check if we setup a scenery path so far
2389 bool addFGDataScenery = globals->get_fg_scenery().empty();
2391 // always add the terrasync location, regardless of whether terrasync
2392 // is enabled or not. This allows us to toggle terrasync on/off at
2393 // runtime and have things work as expected
2394 const PathList& scenery_paths(globals->get_fg_scenery());
2395 if (std::find(scenery_paths.begin(), scenery_paths.end(), terrasyncDir) == scenery_paths.end()) {
2396 // terrasync dir is not in the scenery paths, add it
2397 globals->append_fg_scenery(terrasyncDir);
2400 if (addFGDataScenery) {
2401 // no scenery paths set at all, use the data in FG_ROOT
2402 // ensure this path is added last
2403 SGPath root(globals->get_fg_root());
2404 root.append("Scenery");
2405 globals->append_fg_scenery(root);
2408 return FG_OPTIONS_OK;
2411 void Options::showUsage() const
2413 fgOptLogLevel( "alert" );
2415 FGLocale *locale = globals->get_locale();
2416 SGPropertyNode options_root;
2418 simgear::requestConsole(); // ensure console is shown on Windows
2422 fgLoadProps("options.xml", &options_root);
2423 } catch (const sg_exception &) {
2424 cout << "Unable to read the help file." << endl;
2425 cout << "Make sure the file options.xml is located in the FlightGear base directory," << endl;
2426 cout << "and the location of the base directory is specified by setting $FG_ROOT or" << endl;
2427 cout << "by adding --fg-root=path as a program argument." << endl;
2432 SGPropertyNode *options = options_root.getNode("options");
2434 SG_LOG( SG_GENERAL, SG_ALERT,
2435 "Error reading options.xml: <options> directive not found." );
2439 if (!locale->loadResource("options"))
2441 cout << "Unable to read the language resource." << endl;
2445 const char* usage = locale->getLocalizedString(options->getStringValue("usage"), "options");
2447 cout << usage << endl;
2450 vector<SGPropertyNode_ptr>section = options->getChildren("section");
2451 for (unsigned int j = 0; j < section.size(); j++) {
2454 vector<SGPropertyNode_ptr>option = section[j]->getChildren("option");
2455 for (unsigned int k = 0; k < option.size(); k++) {
2457 SGPropertyNode *name = option[k]->getNode("name");
2458 SGPropertyNode *short_name = option[k]->getNode("short");
2459 SGPropertyNode *key = option[k]->getNode("key");
2460 SGPropertyNode *arg = option[k]->getNode("arg");
2461 bool brief = option[k]->getNode("brief") != 0;
2463 if ((brief || p->verbose) && name) {
2464 string tmp = name->getStringValue();
2468 tmp.append(key->getStringValue());
2472 tmp.append(arg->getStringValue());
2476 tmp.append(short_name->getStringValue());
2479 if (tmp.size() <= 25) {
2482 msg.append( 27-tmp.size(), ' ');
2486 msg.append(32, ' ');
2488 // There may be more than one <description> tag associated
2491 vector<SGPropertyNode_ptr> desc;
2492 desc = option[k]->getChildren("description");
2493 if (! desc.empty()) {
2494 for ( unsigned int l = 0; l < desc.size(); l++) {
2495 string t = desc[l]->getStringValue();
2497 // There may be more than one translation line.
2498 vector<SGPropertyNode_ptr>trans_desc = locale->getLocalizedStrings(t.c_str(),"options");
2499 for ( unsigned int m = 0; m < trans_desc.size(); m++ ) {
2500 string t_str = trans_desc[m]->getStringValue();
2502 if ((m > 0) || ((l > 0) && m == 0)) {
2503 msg.append( 32, ' ');
2506 // If the string is too large to fit on the screen,
2507 // then split it up in several pieces.
2509 while ( t_str.size() > 47 ) {
2511 string::size_type m = t_str.rfind(' ', 47);
2512 msg += t_str.substr(0, m) + '\n';
2513 msg.append( 32, ' ');
2515 t_str.erase(t_str.begin(), t_str.begin() + m + 1);
2517 msg += t_str + '\n';
2524 const char* name = locale->getLocalizedString(section[j]->getStringValue("name"),"options");
2525 if (!msg.empty() && name) {
2526 cout << endl << name << ":" << endl;
2532 if ( !p->verbose ) {
2533 const char* verbose_help = locale->getLocalizedString(options->getStringValue("verbose-help"),"options");
2535 cout << endl << verbose_help << endl;
2538 std::cout << "Hit a key to continue..." << std::endl;
2543 #if defined(__CYGWIN__)
2544 SGPath Options::platformDefaultRoot() const
2546 return SGPath::fromUtf8("../data");
2549 #elif defined(SG_WINDOWS)
2550 SGPath Options::platformDefaultRoot() const
2552 return SGPath::fromUtf8("..\\data");
2554 #elif defined(SG_MAC)
2555 // platformDefaultRoot defined in CocoaHelpers.mm
2557 SGPath Options::platformDefaultRoot() const
2559 return SGPath::fromUtf8(PKGLIBDIR);
2563 void Options::setupRoot(int argc, char **argv)
2566 bool usingDefaultRoot = false;
2568 if (isOptionSet("fg-root")) {
2569 root = SGPath::fromLocal8Bit(valueForOption("fg-root").c_str()); // easy!
2570 SG_LOG(SG_GENERAL, SG_INFO, "set from command-line argument: fg_root = " << root );
2572 // Next check if fg-root is set as an env variable
2573 char *envp = ::getenv( "FG_ROOT" );
2574 if ( envp != NULL ) {
2575 root = SGPath::fromLocal8Bit(envp);
2576 SG_LOG(SG_GENERAL, SG_INFO, "set from FG_ROOT env var: fg_root = " << root );
2578 #if defined(HAVE_QT)
2579 flightgear::initApp(argc, argv);
2580 root = SetupRootDialog::restoreUserSelectedRoot();
2582 if (root.isNull()) {
2583 usingDefaultRoot = true;
2584 root = platformDefaultRoot();
2585 SG_LOG(SG_GENERAL, SG_INFO, "platform default fg_root = " << root );
2587 SG_LOG(SG_GENERAL, SG_INFO, "Qt launcher set fg_root = " << root );
2592 globals->set_fg_root(root);
2593 static char required_version[] = FLIGHTGEAR_VERSION;
2594 string base_version = fgBasePackageVersion(root);
2596 #if defined(HAVE_QT)
2597 // note we never end up here if restoring a user selected root via
2598 // the Qt GUI, since that code pre-validates the path. But if we're using
2599 // a command-line, env-var or default root this check can fail and
2600 // we still want to use the GUI in that case
2601 if (base_version != required_version) {
2602 flightgear::initApp(argc, argv);
2603 SetupRootDialog::runDialog(usingDefaultRoot);
2607 if (base_version.empty()) {
2608 flightgear::fatalMessageBox("Base package not found",
2609 "Required data files not found, check your installation.",
2610 "Looking for base-package files at: '" + root.str() + "'");
2615 if (base_version != required_version) {
2616 flightgear::fatalMessageBox("Base package version mismatch",
2617 "Version check failed: please check your installation.",
2618 "Found data files for version '" + base_version +
2619 "' at '" + globals->get_fg_root().str() + "', version '"
2620 + required_version + "' is required.");
2627 bool Options::shouldLoadDefaultConfig() const
2629 return p->shouldLoadDefaultConfig;
2632 bool Options::checkForArg(int argc, char* argv[], const char* checkArg)
2634 for (int i = 0; i < argc; ++i) {
2635 char* arg = argv[i];
2640 if (*arg != '-') { // we only care about args with a leading hypen
2645 if (*arg == '-') { // skip double hypens
2649 if (strcmp(arg, checkArg) == 0) {
2657 } // of namespace flightgear