DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
src/misc/setup.cpp
00001 /*
00002  *  Copyright (C) 2002-2013  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 
00020 #include "dosbox.h"
00021 #include "cross.h"
00022 #include "setup.h"
00023 #include "control.h"
00024 #include "support.h"
00025 #include <fstream>
00026 #include <string>
00027 #include <sstream>
00028 #include <list>
00029 #include <stdlib.h>
00030 #include <stdio.h>
00031 #include <limits>
00032 #include <limits.h>
00033 
00034 #if defined(_MSC_VER)
00035 # pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */
00036 # pragma warning(disable:4267) /* ... possible loss of data */
00037 # pragma warning(disable:4305) /* truncation from double to float */
00038 #endif
00039 
00040 /* functions to call when DOSBox-X is exiting. */
00041 std::list<Function_wrapper> exitfunctions;
00042 
00043 /* VM events */
00044 std::list<Function_wrapper> vm_event_functions[VM_EVENT_MAX];
00045 
00046 using namespace std;
00047 static std::string current_config_dir; // Set by parseconfigfile so Prop_path can use it to construct the realpath
00048 void Value::destroy() {
00049     if (type == V_STRING) delete _string;
00050     _string = NULL;
00051 }
00052 
00053 Value& Value::copy(Value const& in) {
00054     if (this != &in) { //Selfassigment!
00055         if(type != V_NONE && type != in.type) throw WrongType();
00056         destroy();
00057         plaincopy(in);
00058     }
00059     return *this;
00060 }
00061 
00062 void Value::plaincopy(Value const& in) {
00063     type = in.type;
00064     _int = in._int;
00065     _double = in._double;
00066     _bool = in._bool;
00067     _hex = in._hex;
00068     if(type == V_STRING) _string = new string(*in._string);
00069 }
00070 
00071 Value::operator bool () const {
00072     if(type != V_BOOL) throw WrongType();
00073     return _bool;
00074 }
00075 
00076 Value::operator Hex () const {
00077     if(type != V_HEX) throw WrongType();
00078     return _hex;
00079 }
00080 
00081 Value::operator int () const {
00082     if(type != V_INT) throw WrongType();
00083     return _int;
00084 }
00085 
00086 Value::operator double () const {
00087     if(type != V_DOUBLE) throw WrongType();
00088     return _double;
00089 }
00090 
00091 Value::operator char const* () const {
00092     if(type != V_STRING) throw WrongType();
00093     return _string->c_str();
00094 }
00095 
00096 bool Value::operator==(Value const& other) {
00097     if(this == &other) return true;
00098     if(type != other.type) return false;
00099     switch(type){
00100         case V_BOOL: 
00101             if(_bool == other._bool) return true;
00102             break;
00103         case V_INT:
00104             if(_int == other._int) return true;
00105             break;
00106         case V_HEX:
00107             if(_hex == other._hex) return true;
00108             break;
00109         case V_DOUBLE:
00110             if(_double == other._double) return true;
00111             break;
00112         case V_STRING:
00113             if((*_string) == (*other._string)) return true;
00114             break;
00115         default:
00116             E_Exit("comparing stuff that doesn't make sense");
00117             break;
00118     }
00119     return false;
00120 }
00121 bool Value::SetValue(string const& in,Etype _type) {
00122     /* Throw exception if the current type isn't the wanted type 
00123      * Unless the wanted type is current.
00124      */
00125     if(_type == V_CURRENT && type == V_NONE) throw WrongType();
00126     if(_type != V_CURRENT) { 
00127         if(type != V_NONE && type != _type) throw WrongType();
00128         type = _type;
00129     }
00130     bool retval = true;
00131     switch(type){
00132         case V_HEX:
00133             retval = set_hex(in);
00134             break;
00135         case V_INT:
00136             retval = set_int(in);
00137             break;
00138         case V_BOOL:
00139             retval = set_bool(in);
00140             break;
00141         case V_STRING:
00142             set_string(in);
00143             break;
00144         case V_DOUBLE:
00145             retval = set_double(in);
00146             break;
00147 
00148         case V_NONE:
00149         case V_CURRENT:
00150         default:
00151             /* Shouldn't happen!/Unhandled */
00152             throw WrongType();
00153             break;
00154     }
00155     return retval;
00156 }
00157 
00158 bool Value::set_hex(std::string const& in) {
00159     istringstream input(in);
00160     input.flags(ios::hex);
00161     Bits result = INT_MIN;
00162     input >> result;
00163     if(result == INT_MIN) return false;
00164     _hex = result;
00165     return true;
00166 }
00167 
00168 bool Value::set_int(string const &in) {
00169     istringstream input(in);
00170     Bits result = INT_MIN;
00171     input >> result;
00172     if(result == INT_MIN) return false;
00173     _int = result;
00174     return true;
00175 }
00176 bool Value::set_double(string const &in) {
00177     istringstream input(in);
00178     double result = std::numeric_limits<double>::infinity();
00179     input >> result;
00180     if(result == std::numeric_limits<double>::infinity()) return false;
00181     _double = result;
00182     return true;
00183 }
00184 
00185 bool Value::set_bool(string const &in) {
00186     istringstream input(in);
00187     string result;
00188     input >> result;
00189     lowcase(result);
00190     _bool = true; // TODO
00191     if(!result.size()) return false;
00192     
00193     if(result=="0" || result=="disabled" || result=="false" || result=="off") {
00194         _bool = false;
00195     } else if(result=="1" || result=="enabled" || result=="true" || result=="on") {
00196         _bool = true;
00197     } else return false;
00198     
00199     return true;
00200 }
00201 
00202 void Value::set_string(string const & in) {
00203     if(!_string) _string = new string();
00204     _string->assign(in);
00205 }
00206 
00207 string Value::ToString() const {
00208     ostringstream oss;
00209     switch(type) {
00210         case V_HEX:
00211             oss.flags(ios::hex);
00212             oss << _hex;
00213             break;
00214         case V_INT:
00215             oss << _int;
00216             break;
00217         case V_BOOL:
00218             oss << boolalpha << _bool;
00219             break;
00220         case V_STRING:
00221             oss << *_string;
00222             break;
00223         case V_DOUBLE:
00224             oss.precision(2);
00225             oss << fixed << _double;
00226             break;
00227         case V_NONE:
00228         case V_CURRENT:
00229         default:
00230             E_Exit("ToString messed up ?");
00231             break;
00232     }
00233     return oss.str();
00234 }
00235 
00236 bool Property::CheckValue(Value const& in, bool warn){
00237     if(suggested_values.empty()) return true;
00238     for(iter it = suggested_values.begin();it != suggested_values.end();it++) {
00239         if ( (*it) == in) { //Match!
00240             return true;
00241         }
00242     }
00243     if(warn) LOG_MSG("\"%s\" is not a valid value for variable: %s.\nIt might now be reset to the default value: %s",in.ToString().c_str(),propname.c_str(),default_value.ToString().c_str());
00244     return false;
00245 }
00246 
00247 /* There are too many reasons I can think of to have similar property names per section
00248  * without tying them together by name. Sticking them in MSG_Add as CONFIG_ + propname
00249  * for help strings is just plain dumb. But... in the event that is still useful, I at
00250  * least left the code conditionally enabled if any part of the code still wants to do
00251  * that. --J.C */
00252 void Property::Set_help(string const& in) {
00253     if (use_global_config_str) {
00254         string result = string("CONFIG_") + propname;
00255         upcase(result);
00256         MSG_Add(result.c_str(),in.c_str());
00257     }
00258     else {
00259         help_string = in;
00260     }
00261 }
00262 
00263 char const* Property::Get_help() {
00264     if (use_global_config_str) {
00265         string result = string("CONFIG_") + propname;
00266         upcase(result);
00267         return MSG_Get(result.c_str());
00268     }
00269 
00270     return help_string.c_str();
00271 }
00272 
00273 
00274 bool Prop_int::CheckValue(Value const& in, bool warn) {
00275     if(suggested_values.empty() && Property::CheckValue(in,warn)) return true;
00276     //No >= and <= in Value type and == is ambigious
00277     int mi = min;
00278     int ma = max;
00279     int va = static_cast<int>(Value(in));
00280     if(mi == -1 && ma == -1) return true;
00281     if (va >= mi && va <= ma) return true;
00282     if(warn) LOG_MSG("%s lies outside the range %s-%s for variable: %s.\nIt might now be reset to the default value: %s",in.ToString().c_str(),min.ToString().c_str(),max.ToString().c_str(),propname.c_str(),default_value.ToString().c_str());
00283     return false;
00284 }
00285 
00286 bool Prop_double::SetValue(std::string const& input){
00287     Value val;
00288     if(!val.SetValue(input,Value::V_DOUBLE)) return false;
00289     return SetVal(val,false,/*warn*/true);
00290 }
00291 
00292 bool Prop_int::SetValue(std::string const& input){;
00293     Value val;
00294     if(!val.SetValue(input,Value::V_INT)) return false;
00295     return SetVal(val,false,/*warn*/true);
00296 }
00297 
00298 bool Prop_string::SetValue(std::string const& input){
00299     //Special version for lowcase stuff
00300     std::string temp(input);
00301     //suggested values always case insensitive. 
00302     //If there are none then it can be paths and such which are case sensitive
00303     if(!suggested_values.empty()) lowcase(temp);
00304     Value val(temp,Value::V_STRING);
00305     return SetVal(val,false,true);
00306 }
00307 bool Prop_string::CheckValue(Value const& in, bool warn){
00308     if(suggested_values.empty()) return true;
00309     for(iter it = suggested_values.begin();it != suggested_values.end();it++) {
00310         if ( (*it) == in) { //Match!
00311             return true;
00312         }
00313         if((*it).ToString() == "%u") {
00314             Bit32u value;
00315             if(sscanf(in.ToString().c_str(),"%u",&value) == 1) {
00316                 return true;
00317             }
00318         }
00319     }
00320     if(warn) LOG_MSG("\"%s\" is not a valid value for variable: %s.\nIt might now be reset to the default value: %s",in.ToString().c_str(),propname.c_str(),default_value.ToString().c_str());
00321     return false;
00322 }
00323 
00324 bool Prop_path::SetValue(std::string const& input){
00325     //Special version to merge realpath with it
00326 
00327     Value val(input,Value::V_STRING);
00328     bool retval = SetVal(val,false,true);
00329 
00330     if(input.empty()) {
00331         realpath = "";
00332         return false;
00333     }
00334     std::string workcopy(input);
00335     Cross::ResolveHomedir(workcopy); //Parse ~ and friends
00336     //Prepend config directory in it exists. Check for absolute paths later
00337     if( current_config_dir.empty()) realpath = workcopy;
00338     else realpath = current_config_dir + CROSS_FILESPLIT + workcopy;
00339     //Absolute paths
00340     if (Cross::IsPathAbsolute(workcopy)) realpath = workcopy;
00341     return retval;
00342 }
00343     
00344 bool Prop_bool::SetValue(std::string const& input){
00345     Value val;
00346     if(!val.SetValue(input,Value::V_BOOL)) return false;
00347     return SetVal(val,false,/*warn*/true);
00348 }
00349 
00350 bool Prop_hex::SetValue(std::string const& input){
00351     Value val;
00352     if(!val.SetValue(input,Value::V_HEX)) return false;
00353     return SetVal(val,false,/*warn*/true);
00354 }
00355 
00356 void Prop_multival::make_default_value(){
00357     Bitu i = 1;
00358     Property *p = section->Get_prop(0);
00359     if(!p) return;
00360 
00361     std::string result = p->Get_Default_Value().ToString();
00362     while( (p = section->Get_prop(i++)) ) {
00363         std::string props = p->Get_Default_Value().ToString();
00364         if(props == "") continue;
00365         result += seperator; result += props;
00366     }
00367     Value val(result,Value::V_STRING);
00368     SetVal(val,false,true,/*init*/true);
00369 }
00370 
00371    
00372 
00373 //TODO checkvalue stuff
00374 bool Prop_multival_remain::SetValue(std::string const& input,bool init) {
00375     Value val(input,Value::V_STRING);
00376     bool retval = SetVal(val,false,true,init);
00377 
00378     std::string local(input);
00379     int i = 0,number_of_properties = 0;
00380     Property *p = section->Get_prop(0);
00381     //No properties in this section. do nothing
00382     if(!p) return false;
00383     
00384     while( (section->Get_prop(number_of_properties)) )
00385         number_of_properties++;
00386     
00387     string::size_type loc = string::npos;
00388     while( (p = section->Get_prop(i++)) ) {
00389         //trim leading seperators
00390         loc = local.find_first_not_of(seperator);
00391         if(loc != string::npos) local.erase(0,loc);
00392         loc = local.find_first_of(seperator);
00393         string in = "";//default value
00394         /* when i == number_of_properties add the total line. (makes more then 
00395          * one string argument possible for parameters of cpu) */
00396         if(loc != string::npos && i < number_of_properties) { //seperator found 
00397             in = local.substr(0,loc);
00398             local.erase(0,loc+1);
00399         } else if(local.size()) { //last argument or last property
00400             in = local;
00401             local = "";
00402         }
00403         //Test Value. If it fails set default
00404         Value valtest (in,p->Get_type());
00405         if(!p->CheckValue(valtest,true)) {
00406             make_default_value();
00407             return false;
00408         }
00409         p->SetValue(in);
00410     }
00411     return retval;
00412 }
00413 
00414 //TODO checkvalue stuff
00415 bool Prop_multival::SetValue(std::string const& input,bool init) {
00416     Value val(input,Value::V_STRING);
00417     bool retval = SetVal(val,false,true,init);
00418 
00419     std::string local(input);
00420     int i = 0;
00421     Property *p = section->Get_prop(0);
00422     //No properties in this section. do nothing
00423     if(!p) return false;
00424     string::size_type loc = string::npos;
00425     while( (p = section->Get_prop(i++)) ) {
00426         //trim leading seperators
00427         loc = local.find_first_not_of(seperator);
00428         if(loc != string::npos) local.erase(0,loc);
00429         loc = local.find_first_of(seperator);
00430         string in = "";//default value
00431         if(loc != string::npos) { //seperator found
00432             in = local.substr(0,loc);
00433             local.erase(0,loc+1);
00434         } else if(local.size()) { //last argument
00435             in = local;
00436             local = "";
00437         }
00438         //Test Value. If it fails set default
00439         Value valtest (in,p->Get_type());
00440         if(!p->CheckValue(valtest,true)) {
00441             make_default_value();
00442             return false;
00443         }
00444         p->SetValue(in);
00445 
00446     }
00447     return retval;
00448 }
00449 
00450 const std::vector<Value>& Property::GetValues() const {
00451     return suggested_values;
00452 }
00453 const std::vector<Value>& Prop_multival::GetValues() const 
00454 {
00455     Property *p = section->Get_prop(0);
00456     //No properties in this section. do nothing
00457     if(!p) return suggested_values;
00458     int i =0;
00459     while( (p = section->Get_prop(i++)) ) {
00460         std::vector<Value> v = p->GetValues();
00461         if(!v.empty()) return p->GetValues();
00462     }
00463     return suggested_values;
00464 }
00465 
00466 void Property::Set_values(const char * const *in) {
00467     Value::Etype type = default_value.type;
00468     int i = 0;
00469     while (in[i]) {
00470         Value val(in[i],type);
00471         suggested_values.push_back(val);
00472         i++;
00473     }
00474 }
00475 
00476 Prop_double* Section_prop::Add_double(string const& _propname, Property::Changeable::Value when, double _value) {
00477     Prop_double* test=new Prop_double(_propname,when,_value);
00478     properties.push_back(test);
00479     return test;
00480 }
00481 
00482 Prop_int* Section_prop::Add_int(string const& _propname, Property::Changeable::Value when, int _value) {
00483     Prop_int* test=new Prop_int(_propname,when,_value);
00484     properties.push_back(test);
00485     return test;
00486 }
00487 
00488 Prop_string* Section_prop::Add_string(string const& _propname, Property::Changeable::Value when, char const * const _value) {
00489     Prop_string* test=new Prop_string(_propname,when,_value);
00490     properties.push_back(test);
00491     return test;
00492 }
00493 
00494 Prop_path* Section_prop::Add_path(string const& _propname, Property::Changeable::Value when, char const * const _value) {
00495     Prop_path* test=new Prop_path(_propname,when,_value);
00496     properties.push_back(test);
00497     return test;
00498 }
00499 
00500 Prop_bool* Section_prop::Add_bool(string const& _propname, Property::Changeable::Value when, bool _value) {
00501     Prop_bool* test=new Prop_bool(_propname,when,_value);
00502     properties.push_back(test);
00503     return test;
00504 }
00505 Prop_hex* Section_prop::Add_hex(string const& _propname, Property::Changeable::Value when, Hex _value) {
00506     Prop_hex* test=new Prop_hex(_propname,when,_value);
00507     properties.push_back(test);
00508     return test;
00509 }
00510 Prop_multival* Section_prop::Add_multi(std::string const& _propname, Property::Changeable::Value when,std::string const& sep) {
00511     Prop_multival* test = new Prop_multival(_propname,when,sep);
00512     properties.push_back(test);
00513     return test;
00514 }
00515 Prop_multival_remain* Section_prop::Add_multiremain(std::string const& _propname, Property::Changeable::Value when,std::string const& sep) {
00516     Prop_multival_remain* test = new Prop_multival_remain(_propname,when,sep);
00517     properties.push_back(test);
00518     return test;
00519 }
00520 
00521 int Section_prop::Get_int(string const&_propname) const {
00522     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00523         if((*tel)->propname==_propname){
00524             return ((*tel)->GetValue());
00525         }
00526     }
00527     return 0;
00528 }
00529 
00530 bool Section_prop::Get_bool(string const& _propname) const {
00531     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00532         if((*tel)->propname==_propname){
00533             return ((*tel)->GetValue());
00534         }
00535     }
00536     return false;
00537 }
00538 double Section_prop::Get_double(string const& _propname) const {
00539     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00540         if((*tel)->propname==_propname){
00541             return ((*tel)->GetValue());
00542         }
00543     }
00544     return 0.0;
00545 }
00546 
00547 Prop_path* Section_prop::Get_path(string const& _propname) const {
00548     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00549         if((*tel)->propname==_propname){
00550             Prop_path* val = dynamic_cast<Prop_path*>((*tel));
00551             if(val) return val; else return NULL;
00552         }
00553     }
00554     return NULL;
00555 }
00556 
00557 Prop_multival* Section_prop::Get_multival(string const& _propname) const {
00558     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00559         if((*tel)->propname==_propname){
00560             Prop_multival* val = dynamic_cast<Prop_multival*>((*tel));
00561             if(val) return val; else return NULL;
00562         }
00563     }
00564     return NULL;
00565 }
00566 
00567 Prop_multival_remain* Section_prop::Get_multivalremain(string const& _propname) const {
00568     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00569         if((*tel)->propname==_propname){
00570             Prop_multival_remain* val = dynamic_cast<Prop_multival_remain*>((*tel));
00571             if(val) return val; else return NULL;
00572         }
00573     }
00574     return NULL;
00575 }
00576 Property* Section_prop::Get_prop(int index){
00577     for(it tel=properties.begin();tel!=properties.end();tel++){
00578         if(!index--) return (*tel);
00579     }
00580     return NULL;
00581 }
00582 
00583 const char* Section_prop::Get_string(string const& _propname) const {
00584     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00585         if((*tel)->propname==_propname){
00586             return ((*tel)->GetValue());
00587         }
00588     }
00589     return "";
00590 }
00591 Hex Section_prop::Get_hex(string const& _propname) const {
00592     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00593         if((*tel)->propname==_propname){
00594             return ((*tel)->GetValue());
00595         }
00596     }
00597     return 0;
00598 }
00599 
00600 void trim(string& in) {
00601     string::size_type loc = in.find_first_not_of(" \r\t\f\n");
00602     if(loc != string::npos) in.erase(0,loc);
00603     loc = in.find_last_not_of(" \r\t\f\n");
00604     if(loc != string::npos) in.erase(loc+1);
00605 }
00606 
00607 //TODO double c_str
00608 bool Section_prop::HandleInputline(string const& gegevens){
00609     string str1 = gegevens;
00610     string::size_type loc = str1.find('=');
00611     if(loc == string::npos) return false;
00612     string name = str1.substr(0,loc);
00613     string val = str1.substr(loc + 1);
00614     /* trim the results incase there were spaces somewhere */
00615     trim(name);trim(val);
00616     for(it tel=properties.begin();tel!=properties.end();tel++){
00617         if(!strcasecmp((*tel)->propname.c_str(),name.c_str())){
00618             if (!((*tel)->SetValue(val))) return false;
00619 
00620             for (std::list<SectionFunction>::iterator i=onpropchange.begin();i!=onpropchange.end();i++)
00621                 (*i)(this);
00622 
00623             return true;
00624         }
00625     }
00626     return false;
00627 }
00628 
00629 void Section_prop::PrintData(FILE* outfile,bool everything) {
00630     /* Now print out the individual section entries */
00631     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00632         if (!everything && !(*tel)->modified()) continue;
00633 
00634         fprintf(outfile,"%s=%s\n",(*tel)->propname.c_str(),(*tel)->GetValue().ToString().c_str());
00635     }
00636 }
00637 
00638 //TODO geen noodzaak voor 2 keer c_str
00639 string Section_prop::GetPropValue(string const& _property) const{
00640     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00641         if(!strcasecmp((*tel)->propname.c_str(),_property.c_str())){
00642             return (*tel)->GetValue().ToString();
00643         }
00644     }
00645     return NO_SUCH_PROPERTY;
00646 }
00647 
00648 /* NTS: For whatever reason, this string concatenation is reported by Valgrind as a memory
00649  *      leak because std::string's destructor is never given a chance to clear it, or something...
00650  *      I can't quite pinpoint why. Not enough information is provided so far in stack traces.
00651  *      It SHOULD be freed, because Section_line is derived from Section, the constructors are
00652  *      virtual, and therefore std::string should get a chance to free it's memory. */
00653 bool Section_line::HandleInputline(string const& line){ 
00654     data+=line;
00655     data+="\n";
00656     return true;
00657 }
00658 
00659 void Section_line::PrintData(FILE* outfile,bool everything) {
00660     (void)everything;//UNUSED
00661     fprintf(outfile,"%s",data.c_str());
00662 }
00663 
00664 string Section_line::GetPropValue(string const& /* _property*/) const {
00665     return NO_SUCH_PROPERTY;
00666 }
00667 
00668 bool Config::PrintConfig(char const * const configfilename,bool everything) const {
00669     char temp[50];char helpline[256];
00670     FILE* outfile=fopen(configfilename,"w+t");
00671     if(outfile==NULL) return false;
00672 
00673     /* Print start of configfile and add an return to improve readibility. */
00674     fprintf(outfile,MSG_Get("CONFIGFILE_INTRO"),VERSION);
00675     fprintf(outfile,"\n");
00676     for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){
00677         /* Print out the Section header */
00678         Section_prop *sec = dynamic_cast<Section_prop *>(*tel);
00679         strcpy(temp,(*tel)->GetName());
00680         lowcase(temp);
00681 
00682         if (sec) {
00683             int mods=0;
00684             Property *p;
00685             size_t i = 0, maxwidth = 0;
00686             while ((p = sec->Get_prop(i++))) {
00687                 if (!everything && !p->modified()) continue;
00688 
00689                 size_t w = strlen(p->propname.c_str());
00690                 if (w > maxwidth) maxwidth = w;
00691                 mods++;
00692             }
00693 
00694             if (!everything && mods == 0) {
00695                 /* nothing to print */
00696                 continue;
00697             }
00698 
00699             fprintf(outfile,"[%s]\n",temp);
00700 
00701             i=0;
00702             char prefix[80];
00703             snprintf(prefix,80, "\n# %*s  ", (int)maxwidth, "");
00704             while ((p = sec->Get_prop(i++))) {
00705                 if (!everything && !p->modified()) continue;
00706 
00707                 std::string help = p->Get_help();
00708                 std::string::size_type pos = std::string::npos;
00709                 while ((pos = help.find("\n", pos+1)) != std::string::npos) {
00710                     help.replace(pos, 1, prefix);
00711                 }
00712 
00713                 std::vector<Value> values = p->GetValues();
00714 
00715                 if (help != "" || !values.empty()) {
00716                     fprintf(outfile, "# %*s: %s", (int)maxwidth, p->propname.c_str(), help.c_str());
00717 
00718                     if (!values.empty()) {
00719                         fprintf(outfile, "%s%s:", prefix, MSG_Get("CONFIG_SUGGESTED_VALUES"));
00720                         std::vector<Value>::iterator it = values.begin();
00721                         while (it != values.end()) {
00722                             if((*it).ToString() != "%u") { //Hack hack hack. else we need to modify GetValues, but that one is const...
00723                                 if (it != values.begin()) fputs(",", outfile);
00724                                 fprintf(outfile, " %s", (*it).ToString().c_str());
00725                             }
00726                             ++it;
00727                         }
00728                         fprintf(outfile,".");
00729                     }
00730                     fprintf(outfile, "\n");
00731                 }
00732             }
00733         } else {
00734             fprintf(outfile,"[%s]\n",temp);
00735 
00736             upcase(temp);
00737             strcat(temp,"_CONFIGFILE_HELP");
00738             const char * helpstr=MSG_Get(temp);
00739             char * helpwrite=helpline;
00740             while (*helpstr) {
00741                 *helpwrite++=*helpstr;
00742                 if (*helpstr == '\n') {
00743                     *helpwrite=0;
00744                     fprintf(outfile,"# %s",helpline);
00745                     helpwrite=helpline;
00746                 }
00747                 helpstr++;
00748             }
00749         }
00750        
00751         (*tel)->PrintData(outfile,everything);
00752         fprintf(outfile,"\n");      /* Always an empty line between sections */
00753     }
00754     fclose(outfile);
00755     return true;
00756 }
00757    
00758 
00759 Section_prop* Config::AddSection_prop(char const * const _name,void (*_initfunction)(Section*),bool canchange){
00760     (void)_initfunction;//UNUSED
00761     (void)canchange;//UNUSED
00762     Section_prop* blah = new Section_prop(_name);
00763     sectionlist.push_back(blah);
00764     return blah;
00765 }
00766 
00767 Section_prop::~Section_prop() {
00768     /* Delete properties themself (properties stores the pointer of a prop */
00769     for(it prop = properties.begin(); prop != properties.end(); prop++) delete (*prop);
00770     properties.clear();
00771 }
00772 
00773 
00774 Section_line* Config::AddSection_line(char const * const _name,void (*_initfunction)(Section*)){
00775     (void)_initfunction;//UNUSED
00776     Section_line* blah = new Section_line(_name);
00777     sectionlist.push_back(blah);
00778     return blah;
00779 }
00780 
00781 void Null_Init(Section *sec);
00782 
00783 void AddExitFunction(SectionFunction func,const char *name,bool canchange) {
00784     /* NTS: Add functions so that iterating front to back executes them in First In Last Out order. */
00785     exitfunctions.push_front(Function_wrapper(func,canchange,name));
00786 }
00787 
00788 void AddVMEventFunction(enum vm_event event,SectionFunction func,const char *name,bool canchange) {
00789     assert(event < VM_EVENT_MAX);
00790 
00791     /* NTS: First In First Out order */
00792     vm_event_functions[event].push_back(Function_wrapper(func,canchange,name));
00793 }
00794 
00795 const char *VM_EVENT_string[VM_EVENT_MAX] = {
00796     "Power On",             // 0
00797     "Reset",
00798     "Reset Complete",
00799     "BIOS Init",
00800     "BIOS Boot",
00801 
00802     "Guest OS Boot",            // 5
00803     "DOS Boot",
00804     "DOS Init, kernel ready",
00805     "DOS Init, CONFIG.SYS done",
00806     "DOS Init, shell ready",
00807 
00808     "DOS Init, AUTOEXEC.BAT done",      // 10
00809     "DOS Init, at promot",
00810     "DOS exit, begin",
00811     "DOS exit, kernel exit",
00812     "DOS exit, reboot begin",
00813 
00814     "DOS exit, kernel reboot exit",     // 15
00815     "DOS surprise reboot"
00816 };
00817 
00818 VMDispatchState vm_dispatch_state;
00819 
00820 const char *GetVMEventName(enum vm_event event) {
00821     if (event >= VM_EVENT_MAX) return "";
00822     return VM_EVENT_string[event];
00823 }
00824 
00825 void DispatchVMEvent(enum vm_event event) {
00826     assert(event < VM_EVENT_MAX);
00827 
00828     LOG(LOG_MISC,LOG_DEBUG)("Dispatching VM event %s",GetVMEventName(event));
00829 
00830     vm_dispatch_state.begin_event(event);
00831     for (std::list<Function_wrapper>::iterator i=vm_event_functions[event].begin();i!=vm_event_functions[event].end();i++) {
00832         LOG(LOG_MISC,LOG_DEBUG)("Calling event %s handler (%p) '%s'",GetVMEventName(event),(void*)((uintptr_t)((*i).function)),(*i).name.c_str());
00833         (*i).function(NULL);
00834     }
00835 
00836     vm_dispatch_state.end_event();
00837 }
00838 
00839 Config::~Config() {
00840     std::list<Section*>::iterator it; // FIXME: You guys do realize C++ STL provides reverse_iterator?
00841 
00842     while ((it=sectionlist.end()) != sectionlist.begin()) {
00843         it--;
00844         delete (*it);
00845         sectionlist.erase(it);
00846     }
00847 }
00848 
00849 Section* Config::GetSection(int index){
00850     for (it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){
00851         if (!index--) return (*tel);
00852     }
00853     return NULL;
00854 }
00855 //c_str() 2x
00856 Section* Config::GetSection(string const& _sectionname) const{
00857     for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){
00858         if (!strcasecmp((*tel)->GetName(),_sectionname.c_str())) return (*tel);
00859     }
00860     return NULL;
00861 }
00862 
00863 Section* Config::GetSectionFromProperty(char const * const prop) const{
00864     for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){
00865         if ((*tel)->GetPropValue(prop) != NO_SUCH_PROPERTY) return (*tel);
00866     }
00867     return NULL;
00868 }
00869 
00870 
00871 bool Config::ParseConfigFile(char const * const configfilename){
00872     LOG(LOG_MISC,LOG_DEBUG)("Attempting to load config file #%zu from %s",configfiles.size(),configfilename);
00873 
00874     //static bool first_configfile = true;
00875     ifstream in(configfilename);
00876     if (!in) return false;
00877     const char * settings_type;
00878     settings_type = (configfiles.size() == 0)? "primary":"additional";
00879     configfiles.push_back(configfilename);
00880     
00881     LOG(LOG_MISC,LOG_NORMAL)("Loading %s settings from config file %s", settings_type,configfilename);
00882 
00883     //Get directory from configfilename, used with relative paths.
00884     current_config_dir=configfilename;
00885     std::string::size_type pos = current_config_dir.rfind(CROSS_FILESPLIT);
00886     if(pos == std::string::npos) pos = 0; //No directory then erase string
00887     current_config_dir.erase(pos);
00888 
00889     string gegevens;
00890     Section* currentsection = NULL;
00891     Section* testsec = NULL;
00892     while (getline(in,gegevens)) {
00893         
00894         /* strip leading/trailing whitespace */
00895         trim(gegevens);
00896         if(!gegevens.size()) continue;
00897 
00898         switch(gegevens[0]){
00899         case '%':
00900         case '\0':
00901         case '#':
00902         case ' ':
00903         case '\n':
00904             continue;
00905             break;
00906         case '[':
00907         {
00908             string::size_type loc = gegevens.find(']');
00909             if(loc == string::npos) continue;
00910             gegevens.erase(loc);
00911             testsec = GetSection(gegevens.substr(1));
00912             currentsection = testsec; /* NTS: If we don't recognize the section we WANT currentsection == NULL so it has no effect */
00913             testsec = NULL;
00914         }
00915             break;
00916         default:
00917             try {
00918                 if(currentsection) currentsection->HandleInputline(gegevens);
00919             } catch(const char* message) {
00920                 message=0;
00921                 //EXIT with message
00922             }
00923             break;
00924         }
00925     }
00926     current_config_dir.clear();//So internal changes don't use the path information
00927     return true;
00928 }
00929 
00930 void Config::ParseEnv(char ** envp) {
00931     for(char** env=envp; *env;env++) {
00932         char copy[1024];
00933         safe_strncpy(copy,*env,1024);
00934         if(strncasecmp(copy,"DOSBOX_",7))
00935             continue;
00936         char* sec_name = &copy[7];
00937         if(!(*sec_name))
00938             continue;
00939         char* prop_name = strrchr(sec_name,'_');
00940         if(!prop_name || !(*prop_name))
00941             continue;
00942         *prop_name++=0;
00943         Section* sect = GetSection(sec_name);
00944         if(!sect)
00945             continue;
00946         sect->HandleInputline(prop_name);
00947     }
00948 }
00949 
00950 bool CommandLine::FindExist(char const * const name,bool remove) {
00951     cmd_it it;
00952     if (!(FindEntry(name,it,false))) return false;
00953     if (remove) cmds.erase(it);
00954     return true;
00955 }
00956 
00957 bool CommandLine::FindHex(char const * const name,int & value,bool remove) {
00958     cmd_it it,it_next;
00959     if (!(FindEntry(name,it,true))) return false;
00960     it_next=it;it_next++;
00961     sscanf((*it_next).c_str(),"%X",(unsigned int*)(&value));
00962     if (remove) cmds.erase(it,++it_next);
00963     return true;
00964 }
00965 
00966 bool CommandLine::FindInt(char const * const name,int & value,bool remove) {
00967     cmd_it it,it_next;
00968     if (!(FindEntry(name,it,true))) return false;
00969     it_next=it;it_next++;
00970     value=atoi((*it_next).c_str());
00971     if (remove) cmds.erase(it,++it_next);
00972     return true;
00973 }
00974 
00975 bool CommandLine::FindString(char const * const name,std::string & value,bool remove) {
00976     cmd_it it,it_next;
00977     if (!(FindEntry(name,it,true))) return false;
00978     it_next=it;it_next++;
00979     value=*it_next;
00980     if (remove) cmds.erase(it,++it_next);
00981     return true;
00982 }
00983 
00984 bool CommandLine::FindCommand(unsigned int which,std::string & value) {
00985     if (which<1) return false;
00986     if (which>cmds.size()) return false;
00987     cmd_it it=cmds.begin();
00988     for (;which>1;which--) it++;
00989     value=(*it);
00990     return true;
00991 }
00992 
00993 bool CommandLine::FindEntry(char const * const name,cmd_it & it,bool neednext) {
00994     for (it=cmds.begin();it!=cmds.end();it++) {
00995         const char *d = (*it).c_str();
00996 
00997         /* HACK: If the search string starts with -, it's a switch,
00998          *       so adjust pointers so that it matches --switch or -switch */
00999         if (*name == '-' && d[0] == '-' && d[1] == '-') d++;
01000 
01001         if (!strcasecmp(d,name)) {
01002             cmd_it itnext=it;itnext++;
01003             if (neednext && (itnext==cmds.end())) return false;
01004             return true;
01005         }
01006     }
01007     return false;
01008 }
01009 
01010 bool CommandLine::FindStringBegin(char const* const begin,std::string & value, bool remove) {
01011     size_t len = strlen(begin);
01012     for (cmd_it it=cmds.begin();it!=cmds.end();it++) {
01013         if (strncmp(begin,(*it).c_str(),len)==0) {
01014             value=((*it).c_str() + len);
01015             if (remove) cmds.erase(it);
01016             return true;
01017         }
01018     }
01019     return false;
01020 }
01021 
01022 bool CommandLine::FindStringRemain(char const * const name,std::string & value) {
01023     cmd_it it;value="";
01024     if (!FindEntry(name,it)) return false;
01025     it++;
01026     for (;it!=cmds.end();it++) {
01027         value+=" ";
01028         value+=(*it);
01029     }
01030     return true;
01031 }
01032 
01033 /* Only used for parsing command.com /C 
01034  * Allowing /C dir and /Cdir
01035  * Restoring quotes back into the commands so command /C mount d "/tmp/a b" works as intended
01036  */
01037 bool CommandLine::FindStringRemainBegin(char const * const name,std::string & value) {
01038     cmd_it it;value="";
01039     if (!FindEntry(name,it)) {
01040         size_t len = strlen(name);
01041             for (it=cmds.begin();it!=cmds.end();it++) {
01042                 if (strncasecmp(name,(*it).c_str(),len)==0) {
01043                     std::string temp = ((*it).c_str() + len);
01044                     //Restore quotes for correct parsing in later stages
01045                     if(temp.find(" ") != std::string::npos)
01046                         value = std::string("\"") + temp + std::string("\"");
01047                     else
01048                         value = temp;
01049                     break;
01050                 }
01051             }
01052         if( it == cmds.end()) return false;
01053     }
01054     it++;
01055     for (;it!=cmds.end();it++) {
01056         value += " ";
01057         std::string temp = (*it);
01058         if(temp.find(" ") != std::string::npos)
01059             value += std::string("\"") + temp + std::string("\"");
01060         else
01061             value += temp;
01062     }
01063     return true;
01064 }
01065 
01066 bool CommandLine::GetStringRemain(std::string & value) {
01067     if(!cmds.size()) return false;
01068         
01069     cmd_it it=cmds.begin();value=(*it++);
01070     for(;it != cmds.end();it++) {
01071         value+=" ";
01072         value+=(*it);
01073     }
01074     return true;
01075 }
01076         
01077 
01078 unsigned int CommandLine::GetCount(void) {
01079     return (unsigned int)cmds.size();
01080 }
01081 
01082 bool CommandLine::GetCurrentArgv(std::string &argv) {
01083     if (opt_scan != cmds.end())
01084         argv = *opt_scan;
01085 
01086     return false;
01087 }
01088 
01089 bool CommandLine::CurrentArgvEnd(void) {
01090     return (opt_scan == cmds.end());
01091 }
01092 
01093 void CommandLine::EatCurrentArgv(void) {
01094     if (opt_scan != cmds.end()) opt_scan = cmds.erase(opt_scan);
01095 }
01096 
01097 void CommandLine::NextArgv(void) {
01098     if (opt_scan != cmds.end()) opt_scan++;
01099 }
01100 
01101 bool CommandLine::NextOptArgv(std::string &argv) {
01102     argv.clear();
01103 
01104     /* no argv to return if we're doing single-char GNU switches */
01105     if (!opt_gnu_getopt_singlechar.empty()) return false;
01106 
01107     if (opt_scan == cmds.end()) return false;
01108     argv = *opt_scan;
01109     if (opt_eat_argv) opt_scan = cmds.erase(opt_scan);
01110     else opt_scan++;
01111     return true;
01112 }
01113 
01114 void CommandLine::ChangeOptStyle(enum opt_style opt_style) {
01115     this->opt_style = opt_style;
01116 }
01117 
01118 bool CommandLine::BeginOpt(bool eat_argv) {
01119     opt_gnu_getopt_singlechar.clear();
01120     opt_scan = cmds.begin();
01121     if (opt_scan == cmds.end()) return false;
01122     opt_eat_argv = eat_argv;
01123     return true;
01124 }
01125 
01126 bool CommandLine::GetOptGNUSingleCharCheck(std::string &name) {
01127     char c;
01128 
01129     /* return another char, skipping spaces or invalid chars */
01130     name.clear();
01131     while (!opt_gnu_getopt_singlechar.empty()) {
01132         c = opt_gnu_getopt_singlechar.at(0);
01133         opt_gnu_getopt_singlechar = opt_gnu_getopt_singlechar.substr(1);
01134         if (c <= ' ' || c > 126) continue;
01135 
01136         name = c;
01137         return true;
01138     }
01139 
01140     return false;
01141 }
01142 
01143 bool CommandLine::GetOpt(std::string &name) {
01144     name.clear();
01145 
01146     /* if we're still doing GNU getopt single-char switches, then parse another and return */
01147     if (GetOptGNUSingleCharCheck(name))
01148         return true;
01149 
01150     while (opt_scan != cmds.end()) {
01151         std::string &argv = *opt_scan;
01152         const char *str = argv.c_str();
01153 
01154         if ((opt_style == CommandLine::either || opt_style == CommandLine::dos) && *str == '/') {
01155             /* MS-DOS style /option. Example: /A /OPT /HAX /BLAH */
01156             name = str+1; /* copy to caller minus leaking slash, then erase/skip */
01157             if (opt_eat_argv) opt_scan = cmds.erase(opt_scan);
01158             else opt_scan++;
01159             return true;
01160         }
01161         else if ((opt_style == CommandLine::either || opt_style == CommandLine::gnu || opt_style == CommandLine::gnu_getopt) && *str == '-') {
01162             str++; /* step past '-' */
01163             if (str[0] == '-' && str[1] == 0) { /* '--' means to stop parsing */
01164                 opt_scan = cmds.end();
01165                 if (opt_eat_argv) opt_scan = cmds.erase(opt_scan);
01166                 break;
01167             }
01168 
01169             if (opt_style == CommandLine::gnu_getopt) {
01170                 /* --switch => "switch"
01171                  * -switch => -s -w -i -t -c -h */
01172                 if (*str == '-') {
01173                     str++;
01174                 }
01175                 else {
01176                     /* assign to single-char parse then eat the argv */
01177                     opt_gnu_getopt_singlechar = str;
01178                     if (opt_eat_argv) opt_scan = cmds.erase(opt_scan);
01179                     else opt_scan++;
01180 
01181                     /* if we parse a single-char switch, great */
01182                     if (GetOptGNUSingleCharCheck(name))
01183                         return true;
01184 
01185                     continue; /* if we're here then there was nothing to parse, continue */
01186                 }
01187             }
01188             else {
01189                 /* -switch and --switch mean the same thing */
01190                 if (*str == '-') str++;
01191             }
01192 
01193             name = str; /* copy to caller, then erase/skip */
01194             if (opt_eat_argv) opt_scan = cmds.erase(opt_scan);
01195             else opt_scan++;
01196             return true;
01197         }
01198         else {
01199             opt_scan++;
01200         }
01201     }
01202 
01203     return false;
01204 }
01205 
01206 void CommandLine::EndOpt() {
01207     opt_scan = cmds.end();
01208 }
01209 
01210 void CommandLine::FillVector(std::vector<std::string> & vector) {
01211     for(cmd_it it=cmds.begin(); it != cmds.end(); it++) {
01212         vector.push_back((*it));
01213     }
01214     // add back the \" if the parameter contained a space
01215     for(Bitu i = 0; i < vector.size(); i++) {
01216         if(vector[i].find(' ') != std::string::npos) {
01217             vector[i] = "\""+vector[i]+"\"";
01218         }
01219     }
01220 }
01221 
01222 int CommandLine::GetParameterFromList(const char* const params[], std::vector<std::string> & output) {
01223     // return values: 0 = P_NOMATCH, 1 = P_NOPARAMS
01224     // TODO return nomoreparams
01225     int retval = 1;
01226     output.clear();
01227     enum {
01228         P_START, P_FIRSTNOMATCH, P_FIRSTMATCH
01229     } parsestate = P_START;
01230     cmd_it it = cmds.begin();
01231     while(it!=cmds.end()) {
01232         bool found = false;
01233         for(Bitu i = 0; params[i] != NULL; i++) {
01234             if (*params[i] == 0) {
01235                 LOG_MSG("FIXME: GetParameterFromList: terminating params[] with \"\" is deprecated. Please terminate the param list with NULL");
01236                 break;
01237             }
01238 
01239             if (!strcasecmp((*it).c_str(),params[i])) {
01240                 // found a parameter
01241                 found = true;
01242                 switch(parsestate) {
01243                 case P_START:
01244                     retval = i+2;
01245                     parsestate = P_FIRSTMATCH;
01246                     break;
01247                 case P_FIRSTMATCH:
01248                 case P_FIRSTNOMATCH:
01249                     return retval;
01250                 }
01251             }
01252         }
01253         if(!found) 
01254             switch(parsestate) {
01255             case P_START:
01256                 retval = 0; // no match
01257                 parsestate = P_FIRSTNOMATCH;
01258                 output.push_back(*it);
01259                 break;
01260             case P_FIRSTMATCH:
01261             case P_FIRSTNOMATCH:
01262                 output.push_back(*it);
01263                 break;
01264             }
01265         cmd_it itold = it;
01266         it++;
01267         cmds.erase(itold);
01268 
01269     }
01270     
01271     return retval;
01272 }
01273 
01274 
01275 CommandLine::CommandLine(int argc,char const * const argv[],enum opt_style opt) {
01276     if (argc>0) {
01277         file_name=argv[0];
01278     }
01279     int i=1;
01280     opt_style = opt;
01281     while (i<argc) {
01282         cmds.push_back(argv[i]);
01283         i++;
01284     }
01285 }
01286 
01287 Bit16u CommandLine::Get_arglength() {
01288     if(cmds.empty()) return 0;
01289     Bit16u i=1;
01290     for(cmd_it it=cmds.begin();it != cmds.end();it++) 
01291         i+=(*it).size() + 1;
01292     return --i;
01293 }
01294 
01295 
01296 CommandLine::CommandLine(char const * const name,char const * const cmdline,enum opt_style opt) {
01297     if (name) file_name=name;
01298     /* Parse the cmds and put them in the list */
01299     bool inword,inquote;char c;
01300     inword=false;inquote=false;
01301     std::string str;
01302     const char * c_cmdline=cmdline;
01303     opt_style = opt;
01304     while ((c=*c_cmdline)!=0) {
01305         if (inquote) {
01306             if (c!='"') str+=c;
01307             else {
01308                 inquote=false;
01309                 cmds.push_back(str);
01310                 str.erase();
01311             }
01312         }else if (inword) {
01313             if (c!=' ') str+=c;
01314             else {
01315                 inword=false;
01316                 cmds.push_back(str);
01317                 str.erase();
01318             }
01319         } 
01320         else if (c=='"') { inquote=true;}
01321         else if (c!=' ') { str+=c;inword=true;}
01322         c_cmdline++;
01323     }
01324     if (inword || inquote) cmds.push_back(str);
01325 }
01326 
01327 void CommandLine::Shift(unsigned int amount) {
01328     while(amount--) {
01329         file_name = cmds.size()?(*(cmds.begin())):"";
01330         if(cmds.size()) cmds.erase(cmds.begin());
01331     }
01332 }