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");
|
||||
|
@ -761,8 +505,9 @@ bool fgInitConfig ( int argc, char **argv ) {
|
|||
}
|
||||
}
|
||||
|
||||
// Scan user config files and command line for a specified aircraft.
|
||||
fgInitFGAircraft(argc, argv);
|
||||
// Scan user config files and command line for a specified aircraft.
|
||||
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,36 +609,12 @@ 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.)
|
||||
if ( !fgInitConfig(argc, argv) ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Config option parsing failed ..." );
|
||||
exit(-1);
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Config option parsing failed ..." );
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Initialize the Window/Graphics environment.
|
||||
|
|
1080
src/Main/options.cxx
1080
src/Main/options.cxx
File diff suppressed because it is too large
Load diff
|
@ -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…
Reference in a new issue