2 // fgcom_init.cxx -- FGCOM configuration parsing and initialization
3 // FGCOM: Copyright (C) H. Wirtz <wirtz@dfn.de>
5 // Adaption of fg_init.cxx from FlightGear
6 // FlightGear: Copyright (C) 1997 Curtis L. Olson - http://www.flightgear.org/~curt
8 // Huge part rewritten by Tobias Ramforth to fit needs of FGCOM.
9 // <tobias@ramforth.com>
12 // This program is free software; you can redistribute it and/or
13 // modify it under the terms of the GNU General Public License as
14 // published by the Free Software Foundation; either version 2 of the
15 // License, or (at your option) any later version.
17 // This program is distributed in the hope that it will be useful, but
18 // WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 // General Public License for more details.
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
31 #if defined( _MSC_VER) || defined(__MINGW32__)
32 # include <direct.h> // for getcwd()
33 # define getcwd _getcwd
37 #include "fgcom_getopt.h"
47 #include "fgcom_init.hxx"
51 #include <simgear/debug/logstream.hxx>
58 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
61 istream& skip_eol( istream& in ) {
63 // skip to end of line.
65 if ( (c == '\n') || (c == '\r') ) {
72 istream& skip_ws( istream& in ) {
75 if ( ! isspace( c ) ) {
76 // put back the non-space character
84 istream& skip_comment( istream& in )
90 if ( in.get( c ) && c != '#' ) {
103 extern const char * default_root;
105 static std::string config;
107 static OptionEntry *fgcomOptionArray = 0;
109 static void _doOptions (int argc, char **argv);
110 static int _parseOption (const std::string & arg, const std::string & next_arg);
111 static void _fgcomParseArgs (int argc, char **argv);
112 static void _fgcomParseOptions (const std::string & path);
114 // Read in configuration (file and command line)
115 bool fgcomInitOptions (const OptionEntry * fgcomOptions, int argc, char **argv)
118 SG_LOG( SG_GENERAL, SG_ALERT, "Error! Uninitialized fgcomOptionArray!" );
124 for (n_options = 0; fgcomOptions[n_options].long_option != NULL; n_options++) {}
126 fgcomOptionArray = (OptionEntry *) realloc (fgcomOptionArray, sizeof (OptionEntry) * (n_options + 1));
127 memcpy (fgcomOptionArray, fgcomOptions, sizeof (OptionEntry) * (n_options + 1));
130 _doOptions (argc, argv);
135 // Create usage information out of fgcomOptionArray
142 if (!fgcomOptionArray)
144 SG_LOG( SG_GENERAL, SG_ALERT, "Error! Options need to be initialized by calling 'fgcomInitConfig'!" );
149 // find longest long_option
151 currentEntry = &fgcomOptionArray[0];
152 while (currentEntry->long_option != 0)
155 current_length = strlen (currentEntry->long_option);
156 if (current_length > max_length)
158 max_length = current_length;
164 max_length += 10; // for "-o, --option=, -option"
167 std::cout << " OPTION" << std::string (max_length - 8,
168 ' ') << "\t\t" << "DESCRIPTION" <<
170 std::cout << std::endl;
172 // iterate through option array
173 currentEntry = &fgcomOptionArray[0];
174 while (currentEntry->long_option != 0)
177 current_length = strlen (currentEntry->long_option);
180 current_length += 10;
182 std::string option = std::string (" -")
183 + std::string (¤tEntry->option);
184 if (option.size() > 4)
185 option = option.substr(0,4);
186 option += std::string (", -")
187 + std::string (currentEntry->long_option)
188 + std::string (", --")
189 + std::string (currentEntry->long_option)
190 + std::string ("=") + std::string (max_length - current_length, ' ');
192 std::cout << option << "\t\t" << currentEntry->description;
194 if (currentEntry->has_param && (currentEntry->type != OPTION_NONE)
195 && (currentEntry->default_value != 0))
197 std::cout << " (default: '";
199 if (currentEntry->type == OPTION_NONE)
203 else if (currentEntry->type == OPTION_BOOL)
205 std::cout << *(bool *) currentEntry->default_value;
207 else if (currentEntry->type == OPTION_STRING)
209 std::cout << (char *) currentEntry->default_value;
211 else if (currentEntry->type == OPTION_FLOAT)
213 std::cout << *(float *) currentEntry->default_value;
215 else if (currentEntry->type == OPTION_DOUBLE)
217 std::cout << *(double *) currentEntry->default_value;
219 else if (currentEntry->type == OPTION_FREQ)
221 std::cout << std::setw (7) << std::
222 setprecision (3) << *(double *) currentEntry->default_value;
224 else if (currentEntry->type == OPTION_INT)
226 std::cout << *(int *) currentEntry->default_value;
228 else if (currentEntry->type == OPTION_CHAR)
230 std::cout << *(char *) currentEntry->default_value;
236 std::cout << std::endl;
241 std::cout << std::endl;
243 std::cout << " Available codecs:" << std::endl;
244 std::cout << " \t" <<
245 "u - ulaw (default and best codec because the mixing is based onto ulaw)"
247 std::cout << " \t" << "a - alaw" << std::endl;
248 std::cout << " \t" << "g - gsm" << std::endl;
249 std::cout << " \t" << "s - speex" << std::endl;
250 std::cout << " \t" << "7 - G.723" << std::endl;
252 std::cout << std::endl;
253 std::cout << std::endl;
255 std::cout << " Mode 1: client for COM1 of flightgear:" << std::endl;
256 std::cout << " \t" << "$ " << prog << std::endl;
257 std::cout << " - connects " << prog << " to fgfs at localhost:" <<
258 DEFAULT_FG_PORT << std::endl;
259 std::cout << " \t" << "$ " << prog << " -sother.host.tld -p23456" <<
261 std::cout << " - connects " << prog << " to fgfs at other.host.tld:23456" << std::endl;
263 std::cout << std::endl;
265 std::cout << " Mode 2: client for an ATC at <airport> on <frequency>:" <<
267 std::cout << " \t" << "$ " << prog << " -aKSFO -f120.500" << std::endl;
268 std::cout << " - sets up " << prog <<
269 " for an ATC radio at KSFO 120.500 MHz" << std::endl;
271 std::cout << std::endl;
272 std::cout << std::endl;
274 std::cout << "Note that " << prog <<
275 " starts with a guest account unless you use -U and -P!" << std::endl;
277 std::cout << std::endl;
281 get_alternate_home(void)
285 char *app_data = getenv("LOCALAPPDATA");
287 ah = _strdup(app_data);
292 ah = strdup (pwd->pw_dir);
297 // Attempt to locate and parse the various non-XML config files in order
298 // from least precidence to greatest precidence
300 _doOptions (int argc, char **argv)
303 homedir = getenv ("HOME");
307 homedir = get_alternate_home();
310 // Check for ~/.fgfsrc
313 config = string (homedir);
316 config.append ("\\");
321 config.append (".fgcomrc");
322 _fgcomParseOptions (config);
325 // Parse remaining command line options
326 // These will override anything specified in a config file
327 _fgcomParseArgs (argc, argv);
331 static std::map<string, string> fgcomOptionMap;
332 static std::map<string, size_t> fgcomLongOptionMap;
334 // Parse a single option
336 _parseOption (const std::string & arg, const std::string & next_arg)
338 if (fgcomLongOptionMap.size () == 0)
341 const OptionEntry * entry = &fgcomOptionArray[0];
342 while (entry->long_option != 0)
344 fgcomLongOptionMap.insert (std::pair < std::string,
346 (std::string (entry->long_option), i));
347 fgcomOptionMap.insert (std::pair < std::string,
349 (std::string (1, entry->option),
350 std::string (entry->long_option)));
357 if ((arg == "--help") || (arg == "-h") || (arg == "-?"))
359 // help/usage request
360 return FGCOM_OPTIONS_HELP;
362 else if ((arg == "--verbose") || (arg == "-v"))
364 // verbose help/usage request
365 return FGCOM_OPTIONS_VERBOSE_HELP;
369 std::map < string, size_t >::iterator it;
370 std::string arg_name, arg_value;
372 if (arg.find ("--") == 0)
375 pos = arg.find ('=');
376 if (pos == string::npos)
378 // did not find a value
379 arg_name = arg.substr (2);
381 // now there are two possibilities:
382 // 1: this is an option without a value
383 // 2: the value can be found in next_arg
384 if (next_arg.empty ())
386 // ok, value cannot be in next_arg
387 SG_LOG( SG_GENERAL, SG_DEBUG, "option '" << arg_name << "'" );
391 if (next_arg.at (0) == '-')
393 // there is no value, new option starts in next_arg
394 SG_LOG( SG_GENERAL, SG_DEBUG, "option '" << arg_name << "'" );
398 // the value is in next_arg
399 arg_value = std::string (next_arg);
400 SG_LOG( SG_GENERAL, SG_DEBUG, "option '" << arg_name << "' with argument '" << arg_value << "'" );
407 arg_name = arg.substr (2, pos - 2);
408 arg_value = arg.substr (pos + 1);
409 SG_LOG( SG_GENERAL, SG_DEBUG, "option '" << arg_name << "' with argument '" << arg_value << "'" );
412 it = fgcomLongOptionMap.find (arg_name);
416 std::map < string, string >::iterator it_b;
417 arg_name = arg.substr (1, 1);
418 arg_value = arg.substr (2);
420 if (arg_name.empty ())
422 SG_LOG (SG_GENERAL, SG_ALERT, "Unknown option '" << arg << "'");
423 return FGCOM_OPTIONS_ERROR;
426 SG_LOG( SG_GENERAL, SG_DEBUG, "option '" << arg_name << "' with argument '" << arg_value << "'" );
428 it_b = fgcomOptionMap.find (arg_name);
430 if (it_b != fgcomOptionMap.end ())
432 it = fgcomLongOptionMap.find (it_b->second);
436 SG_LOG( SG_GENERAL, SG_ALERT, "Unknown option '" << arg << "'" );
437 return FGCOM_OPTIONS_ERROR;
441 if (it != fgcomLongOptionMap.end ())
444 entry = &fgcomOptionArray[it->second];
448 *(bool *) entry->parameter = true;
451 if (entry->has_param && !arg_value.empty ())
453 *(char **) entry->parameter = strdup (arg_value.c_str ());
455 else if (entry->has_param)
457 SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
458 return FGCOM_OPTIONS_ERROR;
462 SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' does not have a parameter" );
463 return FGCOM_OPTIONS_ERROR;
467 if (!arg_value.empty ())
470 float temp = atof(arg_value.c_str ());
475 temp = strtof (arg_value.c_str (), &end);
481 SG_LOG( SG_GENERAL, SG_ALERT, "Cannot parse float value '" << arg_value << "' for option " << arg_name << "!" );
482 return FGCOM_OPTIONS_ERROR;
484 #endif // _MSC_VER y/n
486 *(float *) (entry->parameter) = temp;
487 if (*(float *) (entry->parameter) != temp
490 SG_LOG( SG_GENERAL, SG_ALERT, "Float value '" << arg_value << "' for option " << arg_name << " out of range!" );
491 return FGCOM_OPTIONS_ERROR;
496 SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
497 return FGCOM_OPTIONS_ERROR;
502 if (!arg_value.empty ())
507 temp = strtod (arg_value.c_str (), &end);
513 SG_LOG( SG_GENERAL, SG_ALERT, "Cannot parse double value '" << arg_value << "' for option " << arg_name << "!" );
514 return FGCOM_OPTIONS_ERROR;
517 *(double *) (entry->parameter) = temp;
518 if (*(double *) (entry->parameter) != temp
521 SG_LOG( SG_GENERAL, SG_ALERT, "Double value '" << arg_value << "' for option " << arg_name << " out of range!" );
522 return FGCOM_OPTIONS_ERROR;
527 SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
528 return FGCOM_OPTIONS_ERROR;
532 if (!arg_value.empty ())
537 temp = strtol (arg_value.c_str (), &end, 0);
543 SG_LOG( SG_GENERAL, SG_ALERT, "Cannot parse integer value '" << arg_value << "' for option " << arg_name << "!" );
544 return FGCOM_OPTIONS_ERROR;
547 *(int *) (entry->parameter) = temp;
548 if (*(int *) (entry->parameter) != temp || errno == ERANGE)
550 SG_LOG( SG_GENERAL, SG_ALERT, "Integer value '" << arg_value << "' for option " << arg_name << " out of range!" );
551 return FGCOM_OPTIONS_ERROR;
556 SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
557 return FGCOM_OPTIONS_ERROR;
561 if (entry->has_param && !arg_value.empty ())
563 if (arg_value.length () > 1)
565 SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a single char as parameter" );
566 return FGCOM_OPTIONS_ERROR;
570 *(char *) entry->parameter = arg_value.c_str ()[0];
573 else if (entry->has_param)
575 SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
576 return FGCOM_OPTIONS_ERROR;
580 SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' does not have a parameter" );
581 return FGCOM_OPTIONS_ERROR;
585 *(bool *) entry->parameter = true;
591 SG_LOG( SG_GENERAL, SG_ALERT, "Unknown option '" << arg << "'" );
592 return FGCOM_OPTIONS_ERROR;
596 return FGCOM_OPTIONS_OK;
599 // Parse the command line options
601 _fgcomParseArgs (int argc, char **argv)
603 SG_LOG( SG_GENERAL, SG_DEBUG, "Processing commandline options" );
605 for (int i = 1; i < argc; i++)
607 std::string arg = std::string (argv[i]);
608 std::string next_arg;
611 next_arg = std::string (argv[i + 1]);
614 if (arg.find ('-') == 0)
623 result = _parseOption (arg, next_arg);
625 if (result == FGCOM_OPTIONS_OK)
629 else if (result == FGCOM_OPTIONS_HELP)
636 SG_LOG( SG_GENERAL, SG_ALERT, "Error parsing commandline options !" );
642 SG_LOG( SG_GENERAL, SG_ALERT, "Successfully parsed commandline options" );
645 // Parse config file options
647 _fgcomParseOptions (const std::string & path)
649 if (is_file_or_directory(path.c_str()) != 1) {
650 SG_LOG( SG_GENERAL, SG_DEBUG, "Error: Unable to open " << path );
655 std::ios_base::openmode mode = std::ios_base::in;
656 in.open(path.c_str(),mode);
657 if (!in.is_open ()) {
658 SG_LOG( SG_GENERAL, SG_DEBUG, "Error: DEBUG: Unable to open " << path );
662 SG_LOG(SG_GENERAL, SG_DEBUG, "Processing config file: " << path );
667 getline (in, line, '\n');
669 // catch extraneous (DOS) line ending character
671 for (i = line.length(); i > 0; i--) {
672 if (line[i - 1] > 32) {
677 line = line.substr(0, i);
679 std::string next_arg;
680 if (_parseOption (line, next_arg) == FGCOM_OPTIONS_ERROR) {
681 SG_LOG( SG_GENERAL, SG_ALERT, "ERROR: Config file parse error: " << path << " '" << line << "'" );
690 /* eof - fgcom_init.cpp */