Restructure option/config handling code, to avoid multiple scans & parses for special options (fg-root/fg-aircraft/aircraft). Push most of the code into a new Options class, inside options.cxx, and clean up various call-sites as a result.
This commit is contained in:
parent
3d544fbc1f
commit
b1c7495fec
9 changed files with 845 additions and 767 deletions
|
@ -38,6 +38,10 @@
|
|||
# include <fpu_control.h>
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <unistd.h> // for gethostname()
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -61,14 +65,8 @@ using std::endl;
|
|||
|
||||
#include "fg_os.hxx"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
char homepath[256] = "";
|
||||
char * homedir = homepath;
|
||||
char *hostname = ::getenv( "COMPUTERNAME" );
|
||||
#else
|
||||
char *homedir = ::getenv( "HOME" );
|
||||
char *hostname = ::getenv( "HOSTNAME" );
|
||||
#endif
|
||||
char *homedir = NULL;
|
||||
char *hostname = NULL;
|
||||
bool free_hostname = false;
|
||||
|
||||
// foreward declaration.
|
||||
|
@ -179,9 +177,23 @@ int main ( int argc, char **argv ) {
|
|||
// Windows has no $HOME aka %HOME%, so we have to construct the full path.
|
||||
// make sure it fits into the buffer. Max. path length is 255, but who knows
|
||||
// what's in these environment variables?
|
||||
char homepath[256] = "";
|
||||
homepath[sizeof(homepath)-1] = 0;
|
||||
strncpy( homepath, ::getenv("APPDATA"), sizeof(homepath)-1 );
|
||||
strncat( homepath, "\\flightgear.org", sizeof(homepath)-strlen(homepath)-1 );
|
||||
|
||||
homedir = strdup(homepath);
|
||||
hostname = ::getenv( "COMPUTERNAME" );
|
||||
#else
|
||||
// Unix(alike) systems
|
||||
char _hostname[256];
|
||||
gethostname(_hostname, 256);
|
||||
hostname = strdup(_hostname);
|
||||
free_hostname = true;
|
||||
|
||||
homedir = ::getenv( "HOME" );
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
#ifdef PTW32_STATIC_LIB
|
||||
|
@ -203,9 +215,6 @@ int main ( int argc, char **argv ) {
|
|||
}
|
||||
initFPE();
|
||||
#endif
|
||||
#ifndef _WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
#if defined(sgi)
|
||||
flush_fpe();
|
||||
|
@ -230,6 +239,7 @@ int main ( int argc, char **argv ) {
|
|||
#if defined( HAVE_BC5PLUS )
|
||||
_control87(MCW_EM, MCW_EM); /* defined in float.h */
|
||||
#endif
|
||||
|
||||
bool fgviewer = false;
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
if (!strcmp("--fgviewer", argv[i])) {
|
||||
|
|
|
@ -121,225 +121,9 @@
|
|||
#include "main.hxx"
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
using std::string;
|
||||
using namespace boost::algorithm;
|
||||
|
||||
extern const char *default_root;
|
||||
|
||||
|
||||
// Scan the command line options for the specified option and return
|
||||
// the value.
|
||||
static string fgScanForOption( const string& option, int argc, char **argv ) {
|
||||
int i = 1;
|
||||
|
||||
if (hostname == NULL)
|
||||
{
|
||||
char _hostname[256];
|
||||
if( gethostname(_hostname, 256) >= 0 ) {
|
||||
hostname = strdup(_hostname);
|
||||
free_hostname = true;
|
||||
}
|
||||
}
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Scanning command line for: " << option );
|
||||
|
||||
int len = option.length();
|
||||
|
||||
while ( i < argc ) {
|
||||
SG_LOG( SG_GENERAL, SG_DEBUG, "argv[" << i << "] = " << argv[i] );
|
||||
|
||||
string arg = argv[i];
|
||||
if ( arg.find( option ) == 0 ) {
|
||||
return arg.substr( len );
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
// Scan the user config files for the specified option and return
|
||||
// the value.
|
||||
static string fgScanForOption( const string& option, const string& path ) {
|
||||
sg_gzifstream in( path );
|
||||
if ( !in.is_open() ) {
|
||||
return "";
|
||||
}
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Scanning " << path << " for: " << option );
|
||||
|
||||
int len = option.length();
|
||||
|
||||
in >> skipcomment;
|
||||
while ( ! in.eof() ) {
|
||||
string line;
|
||||
getline( in, line, '\n' );
|
||||
|
||||
// catch extraneous (DOS) line ending character
|
||||
if ( line[line.length() - 1] < 32 ) {
|
||||
line = line.substr( 0, line.length()-1 );
|
||||
}
|
||||
|
||||
if ( line.find( option ) == 0 ) {
|
||||
return line.substr( len );
|
||||
}
|
||||
|
||||
in >> skipcomment;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
// Scan the user config files for the specified option and return
|
||||
// the value.
|
||||
static string fgScanForOption( const string& option ) {
|
||||
string arg("");
|
||||
|
||||
#if defined( unix ) || defined( __CYGWIN__ ) || defined(_MSC_VER)
|
||||
// Next check home directory for .fgfsrc.hostname file
|
||||
if ( arg.empty() ) {
|
||||
if ( homedir != NULL && hostname != NULL && strlen(hostname) > 0) {
|
||||
SGPath config( homedir );
|
||||
config.append( ".fgfsrc" );
|
||||
config.concat( "." );
|
||||
config.concat( hostname );
|
||||
arg = fgScanForOption( option, config.str() );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Next check home directory for .fgfsrc file
|
||||
if ( arg.empty() ) {
|
||||
if ( homedir != NULL ) {
|
||||
SGPath config( homedir );
|
||||
config.append( ".fgfsrc" );
|
||||
arg = fgScanForOption( option, config.str() );
|
||||
}
|
||||
}
|
||||
|
||||
if ( arg.empty() ) {
|
||||
// Check for $fg_root/system.fgfsrc
|
||||
SGPath config( globals->get_fg_root() );
|
||||
config.append( "system.fgfsrc" );
|
||||
arg = fgScanForOption( option, config.str() );
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
|
||||
// Read in configuration (files and command line options) but only set
|
||||
// fg_root and aircraft_paths, which are needed *before* do_options() is called
|
||||
// in fgInitConfig
|
||||
|
||||
bool fgInitFGRoot ( int argc, char **argv ) {
|
||||
string root;
|
||||
|
||||
// First parse command line options looking for --fg-root=, this
|
||||
// will override anything specified in a config file
|
||||
root = fgScanForOption( "--fg-root=", argc, argv);
|
||||
|
||||
// Check in one of the user configuration files.
|
||||
if (root.empty() )
|
||||
root = fgScanForOption( "--fg-root=" );
|
||||
|
||||
// Next check if fg-root is set as an env variable
|
||||
if ( root.empty() ) {
|
||||
char *envp = ::getenv( "FG_ROOT" );
|
||||
if ( envp != NULL ) {
|
||||
root = envp;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, default to a random compiled-in location if we can't
|
||||
// find fg-root any other way.
|
||||
if ( root.empty() ) {
|
||||
#if defined( __CYGWIN__ )
|
||||
root = "../data";
|
||||
#elif defined( _WIN32 )
|
||||
root = "..\\data";
|
||||
#elif defined(__APPLE__)
|
||||
/*
|
||||
The following code looks for the base package inside the application
|
||||
bundle, in the standard Contents/Resources location.
|
||||
*/
|
||||
CFURLRef resourcesUrl = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
|
||||
|
||||
// look for a 'data' subdir
|
||||
CFURLRef dataDir = CFURLCreateCopyAppendingPathComponent(NULL, resourcesUrl, CFSTR("data"), true);
|
||||
|
||||
// now convert down to a path, and the a c-string
|
||||
CFStringRef path = CFURLCopyFileSystemPath(dataDir, kCFURLPOSIXPathStyle);
|
||||
root = CFStringGetCStringPtr(path, CFStringGetSystemEncoding());
|
||||
|
||||
CFRelease(resourcesUrl);
|
||||
CFRelease(dataDir);
|
||||
CFRelease(path);
|
||||
#else
|
||||
root = PKGLIBDIR;
|
||||
#endif
|
||||
}
|
||||
|
||||
SG_LOG(SG_INPUT, SG_INFO, "fg_root = " << root );
|
||||
globals->set_fg_root(root);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Read in configuration (files and command line options) but only set
|
||||
// aircraft
|
||||
bool fgInitFGAircraft ( int argc, char **argv ) {
|
||||
|
||||
string aircraftDir = fgScanForOption("--fg-aircraft=", argc, argv);
|
||||
if (aircraftDir.empty()) {
|
||||
aircraftDir = fgScanForOption("--fg-aircraft=");
|
||||
}
|
||||
|
||||
const char* envp = ::getenv("FG_AIRCRAFT");
|
||||
if (aircraftDir.empty() && envp) {
|
||||
globals->append_aircraft_paths(envp);
|
||||
}
|
||||
|
||||
if (!aircraftDir.empty()) {
|
||||
globals->append_aircraft_paths(aircraftDir);
|
||||
}
|
||||
|
||||
string aircraft;
|
||||
|
||||
// First parse command line options looking for --aircraft=, this
|
||||
// will override anything specified in a config file
|
||||
aircraft = fgScanForOption( "--aircraft=", argc, argv );
|
||||
if ( aircraft.empty() ) {
|
||||
// check synonym option
|
||||
aircraft = fgScanForOption( "--vehicle=", argc, argv );
|
||||
}
|
||||
|
||||
// Check in one of the user configuration files.
|
||||
if ( aircraft.empty() ) {
|
||||
aircraft = fgScanForOption( "--aircraft=" );
|
||||
}
|
||||
if ( aircraft.empty() ) {
|
||||
aircraft = fgScanForOption( "--vehicle=" );
|
||||
}
|
||||
|
||||
// if an aircraft was specified, set the property name
|
||||
if ( !aircraft.empty() ) {
|
||||
SG_LOG(SG_INPUT, SG_INFO, "aircraft = " << aircraft );
|
||||
fgSetString("/sim/aircraft", aircraft.c_str() );
|
||||
} else {
|
||||
SG_LOG(SG_INPUT, SG_INFO, "No user specified aircraft, using default" );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Return the current base package version
|
||||
string fgBasePackageVersion() {
|
||||
|
@ -477,45 +261,6 @@ bool fgDetectLanguage() {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Attempt to locate and parse the various non-XML config files in order
|
||||
// from least precidence to greatest precidence
|
||||
static void
|
||||
do_options (int argc, char ** argv)
|
||||
{
|
||||
// Check for $fg_root/system.fgfsrc
|
||||
SGPath config( globals->get_fg_root() );
|
||||
config.append( "system.fgfsrc" );
|
||||
fgParseOptions(config.str());
|
||||
|
||||
#if defined( unix ) || defined( __CYGWIN__ ) || defined(_MSC_VER)
|
||||
if( hostname != NULL && strlen(hostname) > 0 ) {
|
||||
config.concat( "." );
|
||||
config.concat( hostname );
|
||||
fgParseOptions(config.str());
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check for ~/.fgfsrc
|
||||
if ( homedir != NULL ) {
|
||||
config.set( homedir );
|
||||
config.append( ".fgfsrc" );
|
||||
fgParseOptions(config.str());
|
||||
}
|
||||
|
||||
#if defined( unix ) || defined( __CYGWIN__ ) || defined(_MSC_VER)
|
||||
if( hostname != NULL && strlen(hostname) > 0 ) {
|
||||
// Check for ~/.fgfsrc.hostname
|
||||
config.concat( "." );
|
||||
config.concat( hostname );
|
||||
fgParseOptions(config.str());
|
||||
}
|
||||
#endif
|
||||
|
||||
// Parse remaining command line options
|
||||
// These will override anything specified in a config file
|
||||
fgParseArgs(argc, argv);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool fgFindAircraftInDir(const SGPath& dirPath, T* obj, bool (T::*pred)(const SGPath& p))
|
||||
{
|
||||
|
@ -709,8 +454,7 @@ private:
|
|||
// Read in configuration (file and command line)
|
||||
bool fgInitConfig ( int argc, char **argv ) {
|
||||
|
||||
// First, set some sane default values
|
||||
fgSetDefaults();
|
||||
flightgear::Options::sharedInstance()->init(argc, argv);
|
||||
|
||||
// Read global preferences from $FG_ROOT/preferences.xml
|
||||
SG_LOG(SG_INPUT, SG_INFO, "Reading global preferences");
|
||||
|
@ -762,7 +506,8 @@ bool fgInitConfig ( int argc, char **argv ) {
|
|||
}
|
||||
|
||||
// Scan user config files and command line for a specified aircraft.
|
||||
fgInitFGAircraft(argc, argv);
|
||||
flightgear::Options::sharedInstance()->initAircraft();
|
||||
|
||||
FindAndCacheAircraft f(&autosave);
|
||||
if (!f.loadAircraft()) {
|
||||
return false;
|
||||
|
@ -772,7 +517,7 @@ bool fgInitConfig ( int argc, char **argv ) {
|
|||
|
||||
// parse options after loading aircraft to ensure any user
|
||||
// overrides of defaults are honored.
|
||||
do_options(argc, argv);
|
||||
flightgear::Options::sharedInstance()->processOptions();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -32,11 +32,6 @@ class SGPropertyNode;
|
|||
class SGTime;
|
||||
class SGPath;
|
||||
|
||||
// Read in configuration (files and command line optoins) but only set
|
||||
// fg_root
|
||||
bool fgInitFGRoot ( int argc, char **argv );
|
||||
|
||||
|
||||
// Return the current base package version
|
||||
std::string fgBasePackageVersion();
|
||||
|
||||
|
|
|
@ -188,7 +188,6 @@ fgviewerMain(int argc, char** argv)
|
|||
|
||||
globals = new FGGlobals;
|
||||
|
||||
fgInitFGRoot(arguments.argc(), arguments.argv());
|
||||
if ( !fgInitConfig(arguments.argc(), arguments.argv()) ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Config option parsing failed ..." );
|
||||
exit(-1);
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
#include <simgear/structure/commands.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
|
@ -236,26 +239,30 @@ void FGGlobals::set_fg_root (const string &root) {
|
|||
simgear::ResourceManager::PRIORITY_DEFAULT);
|
||||
}
|
||||
|
||||
void FGGlobals::set_fg_scenery (const string &scenery)
|
||||
void FGGlobals::append_fg_scenery (const string &paths)
|
||||
{
|
||||
SGPath s;
|
||||
if (scenery.empty()) {
|
||||
s.set( fg_root );
|
||||
s.append( "Scenery" );
|
||||
} else
|
||||
s.set( scenery );
|
||||
|
||||
string_list path_list = sgPathSplit( s.str() );
|
||||
fg_scenery.clear();
|
||||
// fg_scenery.clear();
|
||||
SGPropertyNode* sim = fgGetNode("/sim", true);
|
||||
|
||||
for (unsigned i = 0; i < path_list.size(); i++) {
|
||||
SGPath path(path_list[i]);
|
||||
// find first unused fg-scenery property in /sim
|
||||
int propIndex = 0;
|
||||
while (sim->getChild("fg-scenery", propIndex) != NULL) {
|
||||
++propIndex;
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const SGPath& path, sgPathSplit( paths )) {
|
||||
if (!path.exists()) {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "scenery path not found:" << path.str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for duplicates
|
||||
string_list::const_iterator ex = std::find(fg_scenery.begin(), fg_scenery.end(), path.str());
|
||||
if (ex != fg_scenery.end()) {
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "skipping duplicate add of scenery path:" << path.str());
|
||||
continue;
|
||||
}
|
||||
|
||||
simgear::Dir dir(path);
|
||||
SGPath terrainDir(dir.file("Terrain"));
|
||||
SGPath objectsDir(dir.file("Objects"));
|
||||
|
@ -280,8 +287,7 @@ void FGGlobals::set_fg_scenery (const string &scenery)
|
|||
fg_scenery.push_back("");
|
||||
|
||||
// make scenery dirs available to Nasal
|
||||
sim->removeChild("fg-scenery", i, false);
|
||||
SGPropertyNode* n = sim->getChild("fg-scenery", i, true);
|
||||
SGPropertyNode* n = sim->getChild("fg-scenery", propIndex++, true);
|
||||
n->setStringValue(path.str());
|
||||
n->setAttribute(SGPropertyNode::WRITE, false);
|
||||
} // of path list iteration
|
||||
|
|
|
@ -194,7 +194,7 @@ public:
|
|||
void set_fg_root (const std::string &root);
|
||||
|
||||
inline const string_list &get_fg_scenery () const { return fg_scenery; }
|
||||
void set_fg_scenery (const std::string &scenery);
|
||||
void append_fg_scenery (const std::string &scenery);
|
||||
|
||||
const string_list& get_aircraft_paths() const { return fg_aircraft_dirs; }
|
||||
void append_aircraft_path(const std::string& path);
|
||||
|
|
|
@ -609,30 +609,6 @@ int fgMainInit( int argc, char **argv ) {
|
|||
upper_case_property("/sim/tower/airport-id");
|
||||
upper_case_property("/autopilot/route-manager/input");
|
||||
|
||||
// Scan the config file(s) and command line options to see if
|
||||
// fg_root was specified (ignore all other options for now)
|
||||
fgInitFGRoot(argc, argv);
|
||||
|
||||
// Check for the correct base package version
|
||||
static char required_version[] = "2.5.0";
|
||||
string base_version = fgBasePackageVersion();
|
||||
if ( !(base_version == required_version) ) {
|
||||
// tell the operator how to use this application
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "" ); // To popup the console on windows
|
||||
cerr << endl << "Base package check failed:" << endl \
|
||||
<< " Version " << base_version << " found at: " \
|
||||
<< globals->get_fg_root() << endl \
|
||||
<< " Version " << required_version << " is required." << endl \
|
||||
<< "Please upgrade/downgrade base package and set the path to your fgdata" << endl \
|
||||
<< "with --fg-root=path_to_your_fgdata" << endl;
|
||||
#ifdef _MSC_VER
|
||||
cerr << "Hit a key to continue..." << endl;
|
||||
cin.get();
|
||||
#endif
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Load the configuration parameters. (Command line options
|
||||
// override config file options. Config file options override
|
||||
// defaults.)
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include <simgear/timing/sg_time.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <math.h> // rint()
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // atof(), atoi()
|
||||
|
@ -61,12 +63,6 @@
|
|||
|
||||
#include <osg/Version>
|
||||
|
||||
using std::string;
|
||||
using std::sort;
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
#if defined( HAVE_VERSION_H ) && HAVE_VERSION_H
|
||||
# include <Include/version.h>
|
||||
# include <simgear/version.h>
|
||||
|
@ -74,8 +70,22 @@ using std::endl;
|
|||
# include <Include/no_version.h>
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
using std::string;
|
||||
using std::sort;
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
#define NEW_DEFAULT_MODEL_HZ 120
|
||||
|
||||
// defined in bootstrap.cxx
|
||||
extern char *homedir;
|
||||
extern char *hostname;
|
||||
|
||||
enum
|
||||
{
|
||||
FG_OPTIONS_OK = 0,
|
||||
|
@ -87,6 +97,8 @@ enum
|
|||
FG_OPTIONS_SHOW_SOUND_DEVICES = 6
|
||||
};
|
||||
|
||||
static flightgear::Options* shared_instance = NULL;
|
||||
|
||||
static double
|
||||
atof( const string& str )
|
||||
{
|
||||
|
@ -108,20 +120,9 @@ static int fgSetupProxy( const char *arg );
|
|||
* in case, we provide some initial sane values here. This method
|
||||
* should be invoked *before* reading any init files.
|
||||
*/
|
||||
void
|
||||
static void
|
||||
fgSetDefaults ()
|
||||
{
|
||||
// set a possibly independent location for scenery data
|
||||
const char *envp = ::getenv( "FG_SCENERY" );
|
||||
|
||||
if ( envp != NULL ) {
|
||||
// fg_root could be anywhere, so default to environmental
|
||||
// variable $FG_ROOT if it is set.
|
||||
globals->set_fg_scenery(envp);
|
||||
} else {
|
||||
// Otherwise, default to Scenery being in $FG_ROOT/Scenery
|
||||
globals->set_fg_scenery("");
|
||||
}
|
||||
|
||||
// Position (deliberately out of range)
|
||||
fgSetDouble("/position/longitude-deg", 9999.0);
|
||||
|
@ -249,7 +250,9 @@ fgSetDefaults ()
|
|||
fgSetString("/sim/version/revision", REVISION);
|
||||
fgSetInt("/sim/version/build-number", HUDSON_BUILD_NUMBER);
|
||||
fgSetString("/sim/version/build-id", HUDSON_BUILD_ID);
|
||||
if( (envp = ::getenv( "http_proxy" )) != NULL )
|
||||
|
||||
char* envp = ::getenv( "http_proxy" );
|
||||
if( envp != NULL )
|
||||
fgSetupProxy( envp );
|
||||
}
|
||||
|
||||
|
@ -740,24 +743,10 @@ fgOptRoc( const char *arg )
|
|||
return FG_OPTIONS_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
fgOptFgRoot( const char *arg )
|
||||
{
|
||||
// this option is dealt with by fgInitFGRoot
|
||||
return FG_OPTIONS_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
fgOptFgScenery( const char *arg )
|
||||
{
|
||||
globals->set_fg_scenery(arg);
|
||||
return FG_OPTIONS_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
fgOptFgAircraft(const char* arg)
|
||||
{
|
||||
// this option is dealt with by fgInitFGAircraft
|
||||
globals->append_fg_scenery(arg);
|
||||
return FG_OPTIONS_OK;
|
||||
}
|
||||
|
||||
|
@ -1224,20 +1213,6 @@ fgOptVersion( const char *arg )
|
|||
return FG_OPTIONS_EXIT;
|
||||
}
|
||||
|
||||
static int
|
||||
fgOptFpe(const char* arg)
|
||||
{
|
||||
// Actually handled in bootstrap.cxx
|
||||
return FG_OPTIONS_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
fgOptFgviewer(const char* arg)
|
||||
{
|
||||
// Actually handled in bootstrap.cxx
|
||||
return FG_OPTIONS_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
fgOptCallSign(const char * arg)
|
||||
{
|
||||
|
@ -1260,7 +1235,54 @@ fgOptCallSign(const char * arg)
|
|||
}
|
||||
|
||||
|
||||
static map<string,size_t> fgOptionMap;
|
||||
// Set a property for the --prop: option. Syntax: --prop:[<type>:]<name>=<value>
|
||||
// <type> can be "double" etc. but also only the first letter "d".
|
||||
// Examples: --prop:alpha=1 --prop:bool:beta=true --prop:d:gamma=0.123
|
||||
static int
|
||||
fgOptSetProperty(const char* raw)
|
||||
{
|
||||
string arg(raw);
|
||||
string::size_type pos = arg.find('=');
|
||||
if (pos == arg.npos || pos == 0 || pos + 1 == arg.size())
|
||||
return FG_OPTIONS_ERROR;
|
||||
|
||||
string name = arg.substr(0, pos);
|
||||
string value = arg.substr(pos + 1);
|
||||
string type;
|
||||
pos = name.find(':');
|
||||
|
||||
if (pos != name.npos && pos != 0 && pos + 1 != name.size()) {
|
||||
type = name.substr(0, pos);
|
||||
name = name.substr(pos + 1);
|
||||
}
|
||||
SGPropertyNode *n = fgGetNode(name.c_str(), true);
|
||||
|
||||
bool writable = n->getAttribute(SGPropertyNode::WRITE);
|
||||
if (!writable)
|
||||
n->setAttribute(SGPropertyNode::WRITE, true);
|
||||
|
||||
bool ret = false;
|
||||
if (type.empty())
|
||||
ret = n->setUnspecifiedValue(value.c_str());
|
||||
else if (type == "s" || type == "string")
|
||||
ret = n->setStringValue(value.c_str());
|
||||
else if (type == "d" || type == "double")
|
||||
ret = n->setDoubleValue(strtod(value.c_str(), 0));
|
||||
else if (type == "f" || type == "float")
|
||||
ret = n->setFloatValue(atof(value.c_str()));
|
||||
else if (type == "l" || type == "long")
|
||||
ret = n->setLongValue(strtol(value.c_str(), 0, 0));
|
||||
else if (type == "i" || type == "int")
|
||||
ret = n->setIntValue(atoi(value.c_str()));
|
||||
else if (type == "b" || type == "bool")
|
||||
ret = n->setBoolValue(value == "true" || atoi(value.c_str()) != 0);
|
||||
|
||||
if (!writable)
|
||||
n->setAttribute(SGPropertyNode::WRITE, false);
|
||||
return ret ? FG_OPTIONS_OK : FG_OPTIONS_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
option has_param type property b_param s_param func
|
||||
|
@ -1289,11 +1311,13 @@ where:
|
|||
argument.
|
||||
*/
|
||||
|
||||
enum OptionType { OPTION_BOOL, OPTION_STRING, OPTION_DOUBLE, OPTION_INT, OPTION_CHANNEL, OPTION_FUNC };
|
||||
enum OptionType { OPTION_BOOL = 0, OPTION_STRING, OPTION_DOUBLE, OPTION_INT, OPTION_CHANNEL, OPTION_FUNC, OPTION_IGNORE };
|
||||
const int OPTION_MULTI = 1 << 17;
|
||||
|
||||
struct OptionDesc {
|
||||
const char *option;
|
||||
bool has_param;
|
||||
enum OptionType type;
|
||||
int type;
|
||||
const char *property;
|
||||
bool b_param;
|
||||
const char *s_param;
|
||||
|
@ -1366,9 +1390,9 @@ struct OptionDesc {
|
|||
{"pitch", true, OPTION_DOUBLE, "/sim/presets/pitch-deg", false, "", 0 },
|
||||
{"glideslope", true, OPTION_DOUBLE, "/sim/presets/glideslope-deg", false, "", 0 },
|
||||
{"roc", true, OPTION_FUNC, "", false, "", fgOptRoc },
|
||||
{"fg-root", true, OPTION_FUNC, "", false, "", fgOptFgRoot },
|
||||
{"fg-scenery", true, OPTION_FUNC, "", false, "", fgOptFgScenery },
|
||||
{"fg-aircraft", true, OPTION_FUNC, "", false, "", fgOptFgAircraft },
|
||||
{"fg-root", true, OPTION_IGNORE, "", false, "", 0 },
|
||||
{"fg-scenery", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptFgScenery },
|
||||
{"fg-aircraft", true, OPTION_IGNORE | OPTION_MULTI, "", false, "", 0 },
|
||||
{"fdm", true, OPTION_STRING, "/sim/flight-model", false, "", 0 },
|
||||
{"aero", true, OPTION_STRING, "/sim/aero", false, "", 0 },
|
||||
{"aircraft-dir", true, OPTION_STRING, "/sim/aircraft-dir", false, "", 0 },
|
||||
|
@ -1460,19 +1484,19 @@ struct OptionDesc {
|
|||
{"trace-read", true, OPTION_FUNC, "", false, "", fgOptTraceRead },
|
||||
{"trace-write", true, OPTION_FUNC, "", false, "", fgOptTraceWrite },
|
||||
{"log-level", true, OPTION_FUNC, "", false, "", fgOptLogLevel },
|
||||
{"view-offset", true, OPTION_FUNC, "", false, "", fgOptViewOffset },
|
||||
{"view-offset", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptViewOffset },
|
||||
{"visibility", true, OPTION_FUNC, "", false, "", fgOptVisibilityMeters },
|
||||
{"visibility-miles", true, OPTION_FUNC, "", false, "", fgOptVisibilityMiles },
|
||||
{"random-wind", false, OPTION_FUNC, "", false, "", fgOptRandomWind },
|
||||
{"wind", true, OPTION_FUNC, "", false, "", fgOptWind },
|
||||
{"wind", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptWind },
|
||||
{"turbulence", true, OPTION_FUNC, "", false, "", fgOptTurbulence },
|
||||
{"ceiling", true, OPTION_FUNC, "", false, "", fgOptCeiling },
|
||||
{"wp", true, OPTION_FUNC, "", false, "", fgOptWp },
|
||||
{"wp", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptWp },
|
||||
{"flight-plan", true, OPTION_STRING, "/autopilot/route-manager/file-path", false, "", NULL },
|
||||
{"config", true, OPTION_FUNC, "", false, "", fgOptConfig },
|
||||
{"config", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptConfig },
|
||||
{"aircraft", true, OPTION_STRING, "/sim/aircraft", false, "", 0 },
|
||||
{"vehicle", true, OPTION_STRING, "/sim/aircraft", false, "", 0 },
|
||||
{"failure", true, OPTION_FUNC, "", false, "", fgOptFailure },
|
||||
{"failure", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptFailure },
|
||||
{"com1", true, OPTION_DOUBLE, "/instrumentation/comm[0]/frequencies/selected-mhz", false, "", 0 },
|
||||
{"com2", true, OPTION_DOUBLE, "/instrumentation/comm[1]/frequencies/selected-mhz", false, "", 0 },
|
||||
{"nav1", true, OPTION_FUNC, "", false, "", fgOptNAV1 },
|
||||
|
@ -1483,214 +1507,273 @@ struct OptionDesc {
|
|||
{"dme", true, OPTION_FUNC, "", false, "", fgOptDME },
|
||||
{"min-status", true, OPTION_STRING, "/sim/aircraft-min-status", false, "all", 0 },
|
||||
{"livery", true, OPTION_FUNC, "", false, "", fgOptLivery },
|
||||
{"ai-scenario", true, OPTION_FUNC, "", false, "", fgOptScenario },
|
||||
{"ai-scenario", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptScenario },
|
||||
{"disable-ai-scenarios", false, OPTION_FUNC, "", false, "", fgOptNoScenarios},
|
||||
{"parking-id", true, OPTION_FUNC, "", false, "", fgOptParking },
|
||||
{"version", false, OPTION_FUNC, "", false, "", fgOptVersion },
|
||||
{"enable-fpe", false, OPTION_FUNC, "", false, "", fgOptFpe},
|
||||
{"fgviewer", false, OPTION_FUNC, "", false, "", fgOptFgviewer},
|
||||
{"enable-fpe", false, OPTION_IGNORE, "", false, "", 0},
|
||||
{"fgviewer", false, OPTION_IGNORE, "", false, "", 0},
|
||||
{"prop", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptSetProperty},
|
||||
{0}
|
||||
};
|
||||
|
||||
|
||||
// Set a property for the --prop: option. Syntax: --prop:[<type>:]<name>=<value>
|
||||
// <type> can be "double" etc. but also only the first letter "d".
|
||||
// Examples: --prop:alpha=1 --prop:bool:beta=true --prop:d:gamma=0.123
|
||||
static bool
|
||||
set_property(const string& arg)
|
||||
namespace flightgear
|
||||
{
|
||||
string::size_type pos = arg.find('=');
|
||||
if (pos == arg.npos || pos == 0 || pos + 1 == arg.size())
|
||||
return false;
|
||||
|
||||
string name = arg.substr(0, pos);
|
||||
string value = arg.substr(pos + 1);
|
||||
string type;
|
||||
pos = name.find(':');
|
||||
|
||||
if (pos != name.npos && pos != 0 && pos + 1 != name.size()) {
|
||||
type = name.substr(0, pos);
|
||||
name = name.substr(pos + 1);
|
||||
}
|
||||
SGPropertyNode *n = fgGetNode(name.c_str(), true);
|
||||
|
||||
bool writable = n->getAttribute(SGPropertyNode::WRITE);
|
||||
if (!writable)
|
||||
n->setAttribute(SGPropertyNode::WRITE, true);
|
||||
|
||||
bool ret = false;
|
||||
if (type.empty())
|
||||
ret = n->setUnspecifiedValue(value.c_str());
|
||||
else if (type == "s" || type == "string")
|
||||
ret = n->setStringValue(value.c_str());
|
||||
else if (type == "d" || type == "double")
|
||||
ret = n->setDoubleValue(strtod(value.c_str(), 0));
|
||||
else if (type == "f" || type == "float")
|
||||
ret = n->setFloatValue(atof(value.c_str()));
|
||||
else if (type == "l" || type == "long")
|
||||
ret = n->setLongValue(strtol(value.c_str(), 0, 0));
|
||||
else if (type == "i" || type == "int")
|
||||
ret = n->setIntValue(atoi(value.c_str()));
|
||||
else if (type == "b" || type == "bool")
|
||||
ret = n->setBoolValue(value == "true" || atoi(value.c_str()) != 0);
|
||||
|
||||
if (!writable)
|
||||
n->setAttribute(SGPropertyNode::WRITE, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// Parse a single option
|
||||
static int
|
||||
parse_option (const string& arg)
|
||||
/**
|
||||
* internal storage of a value->option binding
|
||||
*/
|
||||
class OptionValue
|
||||
{
|
||||
if ( fgOptionMap.size() == 0 ) {
|
||||
size_t i = 0;
|
||||
OptionDesc *pt = &fgOptionArray[ 0 ];
|
||||
while ( pt->option != 0 ) {
|
||||
fgOptionMap[ pt->option ] = i;
|
||||
i += 1;
|
||||
pt += 1;
|
||||
public:
|
||||
OptionValue(OptionDesc* d, const string& v) :
|
||||
desc(d), value(v)
|
||||
{;}
|
||||
|
||||
OptionDesc* desc;
|
||||
string value;
|
||||
};
|
||||
|
||||
typedef std::vector<OptionValue> OptionValueVec;
|
||||
typedef std::map<string, OptionDesc*> OptionDescDict;
|
||||
|
||||
class Options::OptionsPrivate
|
||||
{
|
||||
public:
|
||||
|
||||
OptionValueVec::const_iterator findValue(const string& key) const
|
||||
{
|
||||
OptionValueVec::const_iterator it = values.begin();
|
||||
for (; it != values.end(); ++it) {
|
||||
if (it->desc->option == key) {
|
||||
return it;
|
||||
}
|
||||
} // of set values iteration
|
||||
|
||||
return it; // not found
|
||||
}
|
||||
|
||||
// General Options
|
||||
if ( (arg == "--help") || (arg == "-h") ) {
|
||||
// help/usage request
|
||||
return(FG_OPTIONS_HELP);
|
||||
} else if ( (arg == "--verbose") || (arg == "-v") ) {
|
||||
// verbose help/usage request
|
||||
return(FG_OPTIONS_VERBOSE_HELP);
|
||||
} else if ( arg.find( "--show-aircraft") == 0) {
|
||||
return(FG_OPTIONS_SHOW_AIRCRAFT);
|
||||
} else if ( arg.find( "--show-sound-devices") == 0) {
|
||||
return(FG_OPTIONS_SHOW_SOUND_DEVICES);
|
||||
} else if ( arg.find( "--prop:" ) == 0 ) {
|
||||
if (!set_property(arg.substr(7))) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Bad property assignment: " << arg );
|
||||
return FG_OPTIONS_ERROR;
|
||||
OptionDesc* findOption(const string& key) const
|
||||
{
|
||||
OptionDescDict::const_iterator it = options.find(key);
|
||||
if (it == options.end()) {
|
||||
return NULL;
|
||||
}
|
||||
} else if ( arg.find("-psn_") == 0) {
|
||||
// on Mac, when launched from the GUI, we are passed the ProcessSerialNumber
|
||||
// as an argument (and no others). Silently ignore the argument here.
|
||||
return FG_OPTIONS_OK;
|
||||
} else if ( arg.find( "--" ) == 0 ) {
|
||||
size_t pos = arg.find( '=' );
|
||||
string arg_name, arg_value;
|
||||
if ( pos == string::npos ) {
|
||||
arg_name = arg.substr( 2 );
|
||||
} else {
|
||||
arg_name = arg.substr( 2, pos - 2 );
|
||||
arg_value = arg.substr( pos + 1);
|
||||
|
||||
return it->second;
|
||||
}
|
||||
map<string,size_t>::iterator it = fgOptionMap.find( arg_name );
|
||||
if ( it != fgOptionMap.end() ) {
|
||||
OptionDesc *pt = &fgOptionArray[ it->second ];
|
||||
switch ( pt->type ) {
|
||||
|
||||
int processOption(OptionDesc* desc, const string& arg_value)
|
||||
{
|
||||
switch ( desc->type & 0xffff ) {
|
||||
case OPTION_BOOL:
|
||||
fgSetBool( pt->property, pt->b_param );
|
||||
fgSetBool( desc->property, desc->b_param );
|
||||
break;
|
||||
case OPTION_STRING:
|
||||
if ( pt->has_param && !arg_value.empty() ) {
|
||||
fgSetString( pt->property, arg_value.c_str() );
|
||||
} else if ( !pt->has_param && arg_value.empty() ) {
|
||||
fgSetString( pt->property, pt->s_param );
|
||||
} else if ( pt->has_param ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
|
||||
if ( desc->has_param && !arg_value.empty() ) {
|
||||
fgSetString( desc->property, arg_value.c_str() );
|
||||
} else if ( !desc->has_param && arg_value.empty() ) {
|
||||
fgSetString( desc->property, desc->s_param );
|
||||
} else if ( desc->has_param ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
|
||||
return FG_OPTIONS_ERROR;
|
||||
} else {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' does not have a parameter" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' does not have a parameter" );
|
||||
return FG_OPTIONS_ERROR;
|
||||
}
|
||||
break;
|
||||
case OPTION_DOUBLE:
|
||||
if ( !arg_value.empty() ) {
|
||||
fgSetDouble( pt->property, atof( arg_value ) );
|
||||
fgSetDouble( desc->property, atof( arg_value ) );
|
||||
} else {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
|
||||
return FG_OPTIONS_ERROR;
|
||||
}
|
||||
break;
|
||||
case OPTION_INT:
|
||||
if ( !arg_value.empty() ) {
|
||||
fgSetInt( pt->property, atoi( arg_value ) );
|
||||
fgSetInt( desc->property, atoi( arg_value ) );
|
||||
} else {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
|
||||
return FG_OPTIONS_ERROR;
|
||||
}
|
||||
break;
|
||||
case OPTION_CHANNEL:
|
||||
// XXX return value of add_channel should be checked?
|
||||
if ( pt->has_param && !arg_value.empty() ) {
|
||||
add_channel( pt->option, arg_value );
|
||||
} else if ( !pt->has_param && arg_value.empty() ) {
|
||||
add_channel( pt->option, pt->s_param );
|
||||
} else if ( pt->has_param ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
|
||||
if ( desc->has_param && !arg_value.empty() ) {
|
||||
add_channel( desc->option, arg_value );
|
||||
} else if ( !desc->has_param && arg_value.empty() ) {
|
||||
add_channel( desc->option, desc->s_param );
|
||||
} else if ( desc->has_param ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
|
||||
return FG_OPTIONS_ERROR;
|
||||
} else {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' does not have a parameter" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' does not have a parameter" );
|
||||
return FG_OPTIONS_ERROR;
|
||||
}
|
||||
break;
|
||||
case OPTION_FUNC:
|
||||
if ( pt->has_param && !arg_value.empty() ) {
|
||||
return pt->func( arg_value.c_str() );
|
||||
} else if ( !pt->has_param && arg_value.empty() ) {
|
||||
return pt->func( pt->s_param );
|
||||
} else if ( pt->has_param ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
|
||||
if ( desc->has_param && !arg_value.empty() ) {
|
||||
return desc->func( arg_value.c_str() );
|
||||
} else if ( !desc->has_param && arg_value.empty() ) {
|
||||
return desc->func( desc->s_param );
|
||||
} else if ( desc->has_param ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
|
||||
return FG_OPTIONS_ERROR;
|
||||
} else {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' does not have a parameter" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' does not have a parameter" );
|
||||
return FG_OPTIONS_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Unknown option '" << arg << "'" );
|
||||
return FG_OPTIONS_ERROR;
|
||||
}
|
||||
} else {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Unknown option '" << arg << "'" );
|
||||
return FG_OPTIONS_ERROR;
|
||||
|
||||
case OPTION_IGNORE:
|
||||
break;
|
||||
}
|
||||
|
||||
return FG_OPTIONS_OK;
|
||||
}
|
||||
|
||||
bool showHelp,
|
||||
verbose,
|
||||
showAircraft;
|
||||
OptionDescDict options;
|
||||
OptionValueVec values;
|
||||
simgear::PathList propertyFiles;
|
||||
};
|
||||
|
||||
// Parse the command line options
|
||||
void
|
||||
fgParseArgs (int argc, char **argv)
|
||||
Options* Options::sharedInstance()
|
||||
{
|
||||
bool in_options = true;
|
||||
bool verbose = false;
|
||||
bool help = false;
|
||||
if (shared_instance == NULL) {
|
||||
shared_instance = new Options;
|
||||
}
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "Processing command line arguments");
|
||||
return shared_instance;
|
||||
}
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
string arg = argv[i];
|
||||
Options::Options() :
|
||||
p(new OptionsPrivate())
|
||||
{
|
||||
p->showHelp = false;
|
||||
p->verbose = false;
|
||||
p->showAircraft = false;
|
||||
|
||||
if (in_options && (arg.find('-') == 0)) {
|
||||
if (arg == "--") {
|
||||
in_options = false;
|
||||
// build option map
|
||||
OptionDesc *desc = &fgOptionArray[ 0 ];
|
||||
while ( desc->option != 0 ) {
|
||||
p->options[ desc->option ] = desc++;
|
||||
}
|
||||
}
|
||||
|
||||
Options::~Options()
|
||||
{
|
||||
}
|
||||
|
||||
void Options::init(int argc, char **argv)
|
||||
{
|
||||
fgSetDefaults();
|
||||
|
||||
// first, process the command line
|
||||
bool inOptions = true;
|
||||
for (int i=1; i<argc; ++i) {
|
||||
if (inOptions && (argv[i][0] == '-')) {
|
||||
if (strcmp(argv[i], "--") == 0) { // end of options delimiter
|
||||
inOptions = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
int result = parseOption(argv[i]);
|
||||
processArgResult(result);
|
||||
} else {
|
||||
int result = parse_option(arg);
|
||||
if ((result == FG_OPTIONS_HELP) || (result == FG_OPTIONS_ERROR))
|
||||
help = true;
|
||||
// XML properties file
|
||||
SGPath f(argv[i]);
|
||||
if (!f.exists()) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "config file not found:" << f.str());
|
||||
return;
|
||||
}
|
||||
|
||||
else if (result == FG_OPTIONS_VERBOSE_HELP)
|
||||
verbose = true;
|
||||
p->propertyFiles.push_back(f);
|
||||
}
|
||||
} // of arguments iteration
|
||||
|
||||
else if (result == FG_OPTIONS_SHOW_AIRCRAFT) {
|
||||
// then config files
|
||||
SGPath config;
|
||||
|
||||
if( homedir && hostname && strlen(hostname) > 0 ) {
|
||||
// Check for ~/.fgfsrc.hostname
|
||||
config.set(homedir);
|
||||
config.append(".fgfsrc");
|
||||
config.concat( "." );
|
||||
config.concat( hostname );
|
||||
readConfig(config);
|
||||
}
|
||||
|
||||
// Check for ~/.fgfsrc
|
||||
if( homedir ) {
|
||||
config.set(homedir);
|
||||
config.append(".fgfsrc");
|
||||
readConfig(config);
|
||||
}
|
||||
|
||||
// setup FG_ROOT
|
||||
setupRoot();
|
||||
|
||||
// system.fgfsrc handling
|
||||
if( hostname && strlen(hostname) > 0 ) {
|
||||
config.set(globals->get_fg_root());
|
||||
config.append( "system.fgfsrc" );
|
||||
config.concat( "." );
|
||||
config.concat( hostname );
|
||||
readConfig(config);
|
||||
}
|
||||
|
||||
config.set(globals->get_fg_root());
|
||||
config.append( "system.fgfsrc" );
|
||||
readConfig(config);
|
||||
}
|
||||
|
||||
void Options::initAircraft()
|
||||
{
|
||||
BOOST_FOREACH(const string& paths, valuesForOption("fg-aircraft")) {
|
||||
globals->append_aircraft_paths(paths);
|
||||
}
|
||||
|
||||
const char* envp = ::getenv("FG_AIRCRAFT");
|
||||
if (envp) {
|
||||
globals->append_aircraft_paths(envp);
|
||||
}
|
||||
|
||||
string aircraft;
|
||||
if (isOptionSet("aircraft")) {
|
||||
aircraft = valueForOption("aircraft");
|
||||
} else if (isOptionSet("vehicle")) {
|
||||
aircraft = valueForOption("vehicle");
|
||||
}
|
||||
|
||||
if (!aircraft.empty()) {
|
||||
SG_LOG(SG_INPUT, SG_INFO, "aircraft = " << aircraft );
|
||||
fgSetString("/sim/aircraft", aircraft.c_str() );
|
||||
} else {
|
||||
SG_LOG(SG_INPUT, SG_INFO, "No user specified aircraft, using default" );
|
||||
}
|
||||
|
||||
if (p->showAircraft) {
|
||||
fgOptLogLevel( "alert" );
|
||||
SGPath path( globals->get_fg_root() );
|
||||
path.append("Aircraft");
|
||||
fgShowAircraft(path);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
void Options::processArgResult(int result)
|
||||
{
|
||||
if ((result == FG_OPTIONS_HELP) || (result == FG_OPTIONS_ERROR))
|
||||
p->showHelp = true;
|
||||
else if (result == FG_OPTIONS_VERBOSE_HELP)
|
||||
p->verbose = true;
|
||||
else if (result == FG_OPTIONS_SHOW_AIRCRAFT) {
|
||||
p->showAircraft = true;
|
||||
} else if (result == FG_OPTIONS_SHOW_SOUND_DEVICES) {
|
||||
SGSoundMgr smgr;
|
||||
|
||||
|
@ -1706,38 +1789,19 @@ fgParseArgs (int argc, char **argv)
|
|||
}
|
||||
devices.clear();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
else if (result == FG_OPTIONS_EXIT)
|
||||
} else if (result == FG_OPTIONS_EXIT) {
|
||||
exit(0);
|
||||
}
|
||||
} else {
|
||||
in_options = false;
|
||||
SG_LOG(SG_GENERAL, SG_INFO,
|
||||
"Reading command-line property file " << arg);
|
||||
readProperties(arg, globals->get_props());
|
||||
}
|
||||
}
|
||||
|
||||
if (help) {
|
||||
fgOptLogLevel( "alert" );
|
||||
fgUsage(verbose);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Finished command line arguments");
|
||||
}
|
||||
|
||||
|
||||
// Parse config file options
|
||||
void
|
||||
fgParseOptions (const string& path) {
|
||||
sg_gzifstream in( path );
|
||||
void Options::readConfig(const SGPath& path)
|
||||
{
|
||||
sg_gzifstream in( path.str() );
|
||||
if ( !in.is_open() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Processing config file: " << path );
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Processing config file: " << path.str() );
|
||||
|
||||
in >> skipcomment;
|
||||
while ( ! in.eof() ) {
|
||||
|
@ -1751,23 +1815,153 @@ fgParseOptions (const string& path) {
|
|||
break;
|
||||
line = line.substr( 0, i );
|
||||
|
||||
if ( parse_option( line ) == FG_OPTIONS_ERROR ) {
|
||||
cerr << endl << "Config file parse error: " << path << " '"
|
||||
if ( parseOption( line ) == FG_OPTIONS_ERROR ) {
|
||||
cerr << endl << "Config file parse error: " << path.str() << " '"
|
||||
<< line << "'" << endl;
|
||||
fgUsage();
|
||||
exit(-1);
|
||||
p->showHelp = true;
|
||||
}
|
||||
in >> skipcomment;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Print usage message
|
||||
void
|
||||
fgUsage (bool verbose)
|
||||
int Options::parseOption(const string& s)
|
||||
{
|
||||
SGPropertyNode *locale = globals->get_locale();
|
||||
if ((s == "--help") || (s=="-h")) {
|
||||
return FG_OPTIONS_HELP;
|
||||
} else if ( (s == "--verbose") || (s == "-v") ) {
|
||||
// verbose help/usage request
|
||||
return FG_OPTIONS_VERBOSE_HELP;
|
||||
} else if (s.find("-psn") == 0) {
|
||||
// on Mac, when launched from the GUI, we are passed the ProcessSerialNumber
|
||||
// as an argument (and no others). Silently ignore the argument here.
|
||||
return FG_OPTIONS_OK;
|
||||
} else if ( s.find( "--show-aircraft") == 0) {
|
||||
return(FG_OPTIONS_SHOW_AIRCRAFT);
|
||||
} else if ( s.find( "--show-sound-devices") == 0) {
|
||||
return(FG_OPTIONS_SHOW_SOUND_DEVICES);
|
||||
} else if ( s.find( "--prop:") == 0) {
|
||||
// property setting has a slightly different syntax, so fudge things
|
||||
OptionDesc* desc = p->findOption("prop");
|
||||
if (s.find("=", 7) == string::npos) { // no equals token
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "malformed property option:" << s);
|
||||
return FG_OPTIONS_ERROR;
|
||||
}
|
||||
|
||||
p->values.push_back(OptionValue(desc, s.substr(7)));
|
||||
return FG_OPTIONS_OK;
|
||||
} else if ( s.find( "--" ) == 0 ) {
|
||||
size_t eqPos = s.find( '=' );
|
||||
string key, value;
|
||||
if (eqPos == string::npos) {
|
||||
key = s.substr(2);
|
||||
} else {
|
||||
key = s.substr( 2, eqPos - 2 );
|
||||
value = s.substr( eqPos + 1);
|
||||
}
|
||||
|
||||
return addOption(key, value);
|
||||
} else {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "unknown option:" << s);
|
||||
return FG_OPTIONS_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
int Options::addOption(const string &key, const string &value)
|
||||
{
|
||||
OptionDesc* desc = p->findOption(key);
|
||||
if (!desc) {
|
||||
return FG_OPTIONS_ERROR;
|
||||
}
|
||||
|
||||
if (!(desc->type & OPTION_MULTI)) {
|
||||
OptionValueVec::const_iterator it = p->findValue(key);
|
||||
if (it != p->values.end()) {
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "multiple values forbidden for option:" << key << ", ignoring:" << value);
|
||||
return FG_OPTIONS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
p->values.push_back(OptionValue(desc, value));
|
||||
return FG_OPTIONS_OK;
|
||||
}
|
||||
|
||||
bool Options::isOptionSet(const string &key) const
|
||||
{
|
||||
OptionValueVec::const_iterator it = p->findValue(key);
|
||||
return (it != p->values.end());
|
||||
}
|
||||
|
||||
string Options::valueForOption(const string& key, const string& defValue) const
|
||||
{
|
||||
OptionValueVec::const_iterator it = p->findValue(key);
|
||||
if (it == p->values.end()) {
|
||||
return defValue;
|
||||
}
|
||||
|
||||
return it->value;
|
||||
}
|
||||
|
||||
string_list Options::valuesForOption(const std::string& key) const
|
||||
{
|
||||
string_list result;
|
||||
OptionValueVec::const_iterator it = p->values.begin();
|
||||
for (; it != p->values.end(); ++it) {
|
||||
if (it->desc->option == key) {
|
||||
result.push_back(it->value);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Options::processOptions()
|
||||
{
|
||||
// now FG_ROOT is setup, process various command line options that bail us
|
||||
// out quickly, but rely on aircraft / root settings
|
||||
if (p->showHelp) {
|
||||
showUsage();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const OptionValue& v, p->values) {
|
||||
int result = p->processOption(v.desc, v.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());
|
||||
continue;
|
||||
}
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_INFO,
|
||||
"Reading command-line property file " << file.str());
|
||||
readProperties(file.str(), globals->get_props());
|
||||
}
|
||||
|
||||
// now options are process, do supplemental fixup
|
||||
const char *envp = ::getenv( "FG_SCENERY" );
|
||||
if (envp) {
|
||||
globals->append_fg_scenery(envp);
|
||||
}
|
||||
|
||||
if (globals->get_fg_scenery().empty()) {
|
||||
// no scenery paths set *at all*, use the data in FG_ROOT
|
||||
SGPath root(globals->get_fg_root());
|
||||
root.append("Scenery");
|
||||
globals->append_fg_scenery(root.str());
|
||||
}
|
||||
}
|
||||
|
||||
void Options::showUsage() const
|
||||
{
|
||||
fgOptLogLevel( "alert" );
|
||||
|
||||
SGPropertyNode *locale = globals->get_locale();
|
||||
SGPropertyNode options_root;
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "" ); // To popup the console on Windows
|
||||
|
@ -1809,7 +2003,7 @@ fgUsage (bool verbose)
|
|||
SGPropertyNode *arg = option[k]->getNode("arg");
|
||||
bool brief = option[k]->getNode("brief") != 0;
|
||||
|
||||
if ((brief || verbose) && name) {
|
||||
if ((brief || p->verbose) && name) {
|
||||
string tmp = name->getStringValue();
|
||||
|
||||
if (key){
|
||||
|
@ -1884,7 +2078,7 @@ fgUsage (bool verbose)
|
|||
}
|
||||
}
|
||||
|
||||
if ( !verbose ) {
|
||||
if ( !p->verbose ) {
|
||||
cout << endl;
|
||||
cout << "For a complete list of options use --help --verbose" << endl;
|
||||
}
|
||||
|
@ -1893,3 +2087,85 @@ fgUsage (bool verbose)
|
|||
cin.get();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__CYGWIN__)
|
||||
string Options::platformDefaultRoot() const
|
||||
{
|
||||
return "../data";
|
||||
}
|
||||
|
||||
#elif defined(_WIN32)
|
||||
string Options::platformDefaultRoot() const
|
||||
{
|
||||
return "..\\data";
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
string Options::platformDefaultRoot() const
|
||||
{
|
||||
/*
|
||||
The following code looks for the base package inside the application
|
||||
bundle, in the standard Contents/Resources location.
|
||||
*/
|
||||
CFURLRef resourcesUrl = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
|
||||
|
||||
// look for a 'data' subdir
|
||||
CFURLRef dataDir = CFURLCreateCopyAppendingPathComponent(NULL, resourcesUrl, CFSTR("data"), true);
|
||||
|
||||
// now convert down to a path, and the a c-string
|
||||
CFStringRef path = CFURLCopyFileSystemPath(dataDir, kCFURLPOSIXPathStyle);
|
||||
string root = CFStringGetCStringPtr(path, CFStringGetSystemEncoding());
|
||||
|
||||
CFRelease(resourcesUrl);
|
||||
CFRelease(dataDir);
|
||||
CFRelease(path);
|
||||
|
||||
return root;
|
||||
}
|
||||
#else
|
||||
string Options::platformDefaultRoot() const
|
||||
{
|
||||
return PKGLIBDIR;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Options::setupRoot()
|
||||
{
|
||||
string root;
|
||||
if (isOptionSet("fg-root")) {
|
||||
root = valueForOption("fg-root"); // easy!
|
||||
} else {
|
||||
// Next check if fg-root is set as an env variable
|
||||
char *envp = ::getenv( "FG_ROOT" );
|
||||
if ( envp != NULL ) {
|
||||
root = envp;
|
||||
} else {
|
||||
root = platformDefaultRoot();
|
||||
}
|
||||
}
|
||||
|
||||
SG_LOG(SG_INPUT, SG_INFO, "fg_root = " << root );
|
||||
globals->set_fg_root(root);
|
||||
|
||||
// validate it
|
||||
static char required_version[] = "2.5.0";
|
||||
string base_version = fgBasePackageVersion();
|
||||
if ( !(base_version == required_version) ) {
|
||||
// tell the operator how to use this application
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "" ); // To popup the console on windows
|
||||
cerr << endl << "Base package check failed:" << endl \
|
||||
<< " Version " << base_version << " found at: " \
|
||||
<< globals->get_fg_root() << endl \
|
||||
<< " Version " << required_version << " is required." << endl \
|
||||
<< "Please upgrade/downgrade base package and set the path to your fgdata" << endl \
|
||||
<< "with --fg-root=path_to_your_fgdata" << endl;
|
||||
#ifdef _MSC_VER
|
||||
cerr << "Hit a key to continue..." << endl;
|
||||
cin.get();
|
||||
#endif
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
} // of namespace flightgear
|
||||
|
||||
|
|
|
@ -24,14 +24,85 @@
|
|||
#ifndef _OPTIONS_HXX
|
||||
#define _OPTIONS_HXX
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#ifndef __cplusplus
|
||||
# error This library requires C++
|
||||
#endif
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
extern void fgSetDefaults ();
|
||||
extern void fgParseArgs (int argc, char ** argv);
|
||||
extern void fgParseOptions (const string &file_path);
|
||||
extern void fgUsage (bool verbose = false);
|
||||
// forward decls
|
||||
class SGPath;
|
||||
|
||||
namespace flightgear
|
||||
{
|
||||
|
||||
class Options
|
||||
{
|
||||
private:
|
||||
Options();
|
||||
|
||||
public:
|
||||
static Options* sharedInstance();
|
||||
|
||||
~Options();
|
||||
|
||||
/**
|
||||
* pass command line arguments, read default config files
|
||||
*/
|
||||
void init(int argc, char* argv[]);
|
||||
|
||||
/**
|
||||
* parse a config file (eg, .fgfsrc)
|
||||
*/
|
||||
void readConfig(const SGPath& path);
|
||||
|
||||
/**
|
||||
* read the value for an option, if it has been set
|
||||
*/
|
||||
std::string valueForOption(const std::string& key, const std::string& defValue = std::string()) const;
|
||||
|
||||
/**
|
||||
* return all values for a multi-valued option
|
||||
*/
|
||||
string_list valuesForOption(const std::string& key) const;
|
||||
|
||||
/**
|
||||
* check if a particular option has been set (so far)
|
||||
*/
|
||||
bool isOptionSet(const std::string& key) const;
|
||||
|
||||
|
||||
/**
|
||||
* set an option value, assuming it is not already set (or multiple values
|
||||
* are permitted)
|
||||
* This can be used to inject option values, eg based upon environment variables
|
||||
*/
|
||||
int addOption(const std::string& key, const std::string& value);
|
||||
|
||||
/**
|
||||
* apply option values to the simulation state
|
||||
* (set properties, etc)
|
||||
*/
|
||||
void processOptions();
|
||||
|
||||
/**
|
||||
* init the aircraft options
|
||||
*/
|
||||
void initAircraft();
|
||||
private:
|
||||
void showUsage() const;
|
||||
|
||||
int parseOption(const std::string& s);
|
||||
|
||||
void processArgResult(int result);
|
||||
|
||||
void setupRoot();
|
||||
|
||||
std::string platformDefaultRoot() const;
|
||||
|
||||
class OptionsPrivate;
|
||||
std::auto_ptr<OptionsPrivate> p;
|
||||
};
|
||||
|
||||
} // of namespace flightgear
|
||||
|
||||
#endif /* _OPTIONS_HXX */
|
||||
|
|
Loading…
Add table
Reference in a new issue