DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
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_double::CheckValue(Value const& in, bool warn)
00293 {
00294         if(suggested_values.empty() && Property::CheckValue(in, warn)) 
00295                 return true;
00296 
00297         const auto mi = static_cast<double>(min);
00298         const auto ma = static_cast<double>(max);
00299         const auto va = static_cast<double>(Value(in));
00300         const auto same = [](const double a, const double b, const double epsilon) {
00301                 return fabs(a - b < epsilon);
00302         };
00303         const auto tolerance = 0.0000001;
00304         
00305         if(same(mi, -1.0, tolerance) && same(ma, -1.0, tolerance)) 
00306                 return true;
00307 
00308         if(va >= mi && va <= ma)
00309                 return true;
00310 
00311         if(warn)
00312                 LOG_MSG(
00313                         "%s lies outside the range %s-%s for variable: %s.\nIt might now be reset to the default value: %s",
00314                         in.ToString().c_str(), 
00315                         min.ToString().c_str(), 
00316                         max.ToString().c_str(), 
00317                         propname.c_str(), 
00318                         default_value.ToString().c_str()
00319                 );
00320 
00321         return false;
00322 }
00323 
00324 bool Prop_int::SetValue(std::string const& input){;
00325     Value val;
00326     if(!val.SetValue(input,Value::V_INT)) return false;
00327     return SetVal(val,false,/*warn*/true);
00328 }
00329 
00330 bool Prop_string::SetValue(std::string const& input){
00331     //Special version for lowcase stuff
00332     std::string temp(input);
00333     //suggested values always case insensitive. 
00334     //If there are none then it can be paths and such which are case sensitive
00335     if(!suggested_values.empty()) lowcase(temp);
00336     Value val(temp,Value::V_STRING);
00337     return SetVal(val,false,true);
00338 }
00339 bool Prop_string::CheckValue(Value const& in, bool warn){
00340     if(suggested_values.empty()) return true;
00341     for(iter it = suggested_values.begin();it != suggested_values.end();it++) {
00342         if ( (*it) == in) { //Match!
00343             return true;
00344         }
00345         if((*it).ToString() == "%u") {
00346             Bit32u value;
00347             if(sscanf(in.ToString().c_str(),"%u",&value) == 1) {
00348                 return true;
00349             }
00350         }
00351     }
00352     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());
00353     return false;
00354 }
00355 
00356 bool Prop_path::SetValue(std::string const& input){
00357     //Special version to merge realpath with it
00358 
00359     Value val(input,Value::V_STRING);
00360     bool retval = SetVal(val,false,true);
00361 
00362     if(input.empty()) {
00363         realpath = "";
00364         return false;
00365     }
00366     std::string workcopy(input);
00367     Cross::ResolveHomedir(workcopy); //Parse ~ and friends
00368     //Prepend config directory in it exists. Check for absolute paths later
00369     if( current_config_dir.empty()) realpath = workcopy;
00370     else realpath = current_config_dir + CROSS_FILESPLIT + workcopy;
00371     //Absolute paths
00372     if (Cross::IsPathAbsolute(workcopy)) realpath = workcopy;
00373     return retval;
00374 }
00375     
00376 bool Prop_bool::SetValue(std::string const& input){
00377     Value val;
00378     if(!val.SetValue(input,Value::V_BOOL)) return false;
00379     return SetVal(val,false,/*warn*/true);
00380 }
00381 
00382 bool Prop_hex::SetValue(std::string const& input){
00383     Value val;
00384     if(!val.SetValue(input,Value::V_HEX)) return false;
00385     return SetVal(val,false,/*warn*/true);
00386 }
00387 
00388 void Prop_multival::make_default_value(){
00389     Bitu i = 1;
00390     Property *p = section->Get_prop(0);
00391     if(!p) return;
00392 
00393     std::string result = p->Get_Default_Value().ToString();
00394     while( (p = section->Get_prop(i++)) ) {
00395         std::string props = p->Get_Default_Value().ToString();
00396         if(props == "") continue;
00397         result += seperator; result += props;
00398     }
00399     Value val(result,Value::V_STRING);
00400     SetVal(val,false,true,/*init*/true);
00401 }
00402 
00403    
00404 
00405 //TODO checkvalue stuff
00406 bool Prop_multival_remain::SetValue(std::string const& input,bool init) {
00407     Value val(input,Value::V_STRING);
00408     bool retval = SetVal(val,false,true,init);
00409 
00410     std::string local(input);
00411     int i = 0,number_of_properties = 0;
00412     Property *p = section->Get_prop(0);
00413     //No properties in this section. do nothing
00414     if(!p) return false;
00415     
00416     while( (section->Get_prop(number_of_properties)) )
00417         number_of_properties++;
00418     
00419     string::size_type loc = string::npos;
00420     while( (p = section->Get_prop(i++)) ) {
00421         //trim leading seperators
00422         loc = local.find_first_not_of(seperator);
00423         if(loc != string::npos) local.erase(0,loc);
00424         loc = local.find_first_of(seperator);
00425         string in = "";//default value
00426         /* when i == number_of_properties add the total line. (makes more then 
00427          * one string argument possible for parameters of cpu) */
00428         if(loc != string::npos && i < number_of_properties) { //seperator found 
00429             in = local.substr(0,loc);
00430             local.erase(0,loc+1);
00431         } else if(local.size()) { //last argument or last property
00432             in = local;
00433             local = "";
00434         }
00435         //Test Value. If it fails set default
00436         Value valtest (in,p->Get_type());
00437         if(!p->CheckValue(valtest,true)) {
00438             make_default_value();
00439             return false;
00440         }
00441         p->SetValue(in);
00442     }
00443     return retval;
00444 }
00445 
00446 //TODO checkvalue stuff
00447 bool Prop_multival::SetValue(std::string const& input,bool init) {
00448     Value val(input,Value::V_STRING);
00449     bool retval = SetVal(val,false,true,init);
00450 
00451     std::string local(input);
00452     int i = 0;
00453     Property *p = section->Get_prop(0);
00454     //No properties in this section. do nothing
00455     if(!p) return false;
00456     string::size_type loc = string::npos;
00457     while( (p = section->Get_prop(i++)) ) {
00458         //trim leading seperators
00459         loc = local.find_first_not_of(seperator);
00460         if(loc != string::npos) local.erase(0,loc);
00461         loc = local.find_first_of(seperator);
00462         string in = "";//default value
00463         if(loc != string::npos) { //seperator found
00464             in = local.substr(0,loc);
00465             local.erase(0,loc+1);
00466         } else if(local.size()) { //last argument
00467             in = local;
00468             local = "";
00469         }
00470         //Test Value. If it fails set default
00471         Value valtest (in,p->Get_type());
00472         if(!p->CheckValue(valtest,true)) {
00473             make_default_value();
00474             return false;
00475         }
00476         p->SetValue(in);
00477 
00478     }
00479     return retval;
00480 }
00481 
00482 const std::vector<Value>& Property::GetValues() const {
00483     return suggested_values;
00484 }
00485 const std::vector<Value>& Prop_multival::GetValues() const 
00486 {
00487     Property *p = section->Get_prop(0);
00488     //No properties in this section. do nothing
00489     if(!p) return suggested_values;
00490     int i =0;
00491     while( (p = section->Get_prop(i++)) ) {
00492         std::vector<Value> v = p->GetValues();
00493         if(!v.empty()) return p->GetValues();
00494     }
00495     return suggested_values;
00496 }
00497 
00498 void Property::Set_values(const char * const *in) {
00499     Value::Etype type = default_value.type;
00500     int i = 0;
00501     while (in[i]) {
00502         Value val(in[i],type);
00503         suggested_values.push_back(val);
00504         i++;
00505     }
00506 }
00507 
00508 Prop_double* Section_prop::Add_double(string const& _propname, Property::Changeable::Value when, double _value) {
00509     Prop_double* test=new Prop_double(_propname,when,_value);
00510     properties.push_back(test);
00511     return test;
00512 }
00513 
00514 Prop_int* Section_prop::Add_int(string const& _propname, Property::Changeable::Value when, int _value) {
00515     Prop_int* test=new Prop_int(_propname,when,_value);
00516     properties.push_back(test);
00517     return test;
00518 }
00519 
00520 Prop_string* Section_prop::Add_string(string const& _propname, Property::Changeable::Value when, char const * const _value) {
00521     Prop_string* test=new Prop_string(_propname,when,_value);
00522     properties.push_back(test);
00523     return test;
00524 }
00525 
00526 Prop_path* Section_prop::Add_path(string const& _propname, Property::Changeable::Value when, char const * const _value) {
00527     Prop_path* test=new Prop_path(_propname,when,_value);
00528     properties.push_back(test);
00529     return test;
00530 }
00531 
00532 Prop_bool* Section_prop::Add_bool(string const& _propname, Property::Changeable::Value when, bool _value) {
00533     Prop_bool* test=new Prop_bool(_propname,when,_value);
00534     properties.push_back(test);
00535     return test;
00536 }
00537 Prop_hex* Section_prop::Add_hex(string const& _propname, Property::Changeable::Value when, Hex _value) {
00538     Prop_hex* test=new Prop_hex(_propname,when,_value);
00539     properties.push_back(test);
00540     return test;
00541 }
00542 Prop_multival* Section_prop::Add_multi(std::string const& _propname, Property::Changeable::Value when,std::string const& sep) {
00543     Prop_multival* test = new Prop_multival(_propname,when,sep);
00544     properties.push_back(test);
00545     return test;
00546 }
00547 Prop_multival_remain* Section_prop::Add_multiremain(std::string const& _propname, Property::Changeable::Value when,std::string const& sep) {
00548     Prop_multival_remain* test = new Prop_multival_remain(_propname,when,sep);
00549     properties.push_back(test);
00550     return test;
00551 }
00552 
00553 int Section_prop::Get_int(string const&_propname) const {
00554     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00555         if((*tel)->propname==_propname){
00556             return ((*tel)->GetValue());
00557         }
00558     }
00559     return 0;
00560 }
00561 
00562 bool Section_prop::Get_bool(string const& _propname) const {
00563     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00564         if((*tel)->propname==_propname){
00565             return ((*tel)->GetValue());
00566         }
00567     }
00568     return false;
00569 }
00570 double Section_prop::Get_double(string const& _propname) const {
00571     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00572         if((*tel)->propname==_propname){
00573             return ((*tel)->GetValue());
00574         }
00575     }
00576     return 0.0;
00577 }
00578 
00579 Prop_path* Section_prop::Get_path(string const& _propname) const {
00580     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00581         if((*tel)->propname==_propname){
00582             Prop_path* val = dynamic_cast<Prop_path*>((*tel));
00583             if(val) return val; else return NULL;
00584         }
00585     }
00586     return NULL;
00587 }
00588 
00589 Prop_multival* Section_prop::Get_multival(string const& _propname) const {
00590     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00591         if((*tel)->propname==_propname){
00592             Prop_multival* val = dynamic_cast<Prop_multival*>((*tel));
00593             if(val) return val; else return NULL;
00594         }
00595     }
00596     return NULL;
00597 }
00598 
00599 Prop_multival_remain* Section_prop::Get_multivalremain(string const& _propname) const {
00600     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00601         if((*tel)->propname==_propname){
00602             Prop_multival_remain* val = dynamic_cast<Prop_multival_remain*>((*tel));
00603             if(val) return val; else return NULL;
00604         }
00605     }
00606     return NULL;
00607 }
00608 Property* Section_prop::Get_prop(int index){
00609     for(it tel=properties.begin();tel!=properties.end();tel++){
00610         if(!index--) return (*tel);
00611     }
00612     return NULL;
00613 }
00614 
00615 const char* Section_prop::Get_string(string const& _propname) const {
00616     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00617         if((*tel)->propname==_propname){
00618             return ((*tel)->GetValue());
00619         }
00620     }
00621     return "";
00622 }
00623 Hex Section_prop::Get_hex(string const& _propname) const {
00624     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00625         if((*tel)->propname==_propname){
00626             return ((*tel)->GetValue());
00627         }
00628     }
00629     return 0;
00630 }
00631 
00632 void trim(string& in) {
00633     string::size_type loc = in.find_first_not_of(" \r\t\f\n");
00634     if(loc != string::npos) in.erase(0,loc);
00635     loc = in.find_last_not_of(" \r\t\f\n");
00636     if(loc != string::npos) in.erase(loc+1);
00637 }
00638 
00639 //TODO double c_str
00640 bool Section_prop::HandleInputline(string const& gegevens){
00641     string str1 = gegevens;
00642     string::size_type loc = str1.find('=');
00643     if(loc == string::npos) return false;
00644     string name = str1.substr(0,loc);
00645     string val = str1.substr(loc + 1);
00646     /* trim the results incase there were spaces somewhere */
00647     trim(name);trim(val);
00648     for(it tel=properties.begin();tel!=properties.end();tel++){
00649         if(!strcasecmp((*tel)->propname.c_str(),name.c_str())){
00650             if (!((*tel)->SetValue(val))) return false;
00651 
00652             for (std::list<SectionFunction>::iterator i=onpropchange.begin();i!=onpropchange.end();i++)
00653                 (*i)(this);
00654 
00655             return true;
00656         }
00657     }
00658     return false;
00659 }
00660 
00661 void Section_prop::PrintData(FILE* outfile,bool everything) {
00662     /* Now print out the individual section entries */
00663     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00664         if (!everything && !(*tel)->modified()) continue;
00665 
00666         fprintf(outfile,"%s=%s\n",(*tel)->propname.c_str(),(*tel)->GetValue().ToString().c_str());
00667     }
00668 }
00669 
00670 //TODO geen noodzaak voor 2 keer c_str
00671 string Section_prop::GetPropValue(string const& _property) const{
00672     for(const_it tel=properties.begin();tel!=properties.end();tel++){
00673         if(!strcasecmp((*tel)->propname.c_str(),_property.c_str())){
00674             return (*tel)->GetValue().ToString();
00675         }
00676     }
00677     return NO_SUCH_PROPERTY;
00678 }
00679 
00680 /* NTS: For whatever reason, this string concatenation is reported by Valgrind as a memory
00681  *      leak because std::string's destructor is never given a chance to clear it, or something...
00682  *      I can't quite pinpoint why. Not enough information is provided so far in stack traces.
00683  *      It SHOULD be freed, because Section_line is derived from Section, the constructors are
00684  *      virtual, and therefore std::string should get a chance to free it's memory. */
00685 bool Section_line::HandleInputline(string const& line){ 
00686     data+=line;
00687     data+="\n";
00688     return true;
00689 }
00690 
00691 void Section_line::PrintData(FILE* outfile,bool everything) {
00692     (void)everything;//UNUSED
00693     fprintf(outfile,"%s",data.c_str());
00694 }
00695 
00696 string Section_line::GetPropValue(string const& /* _property*/) const {
00697     return NO_SUCH_PROPERTY;
00698 }
00699 
00700 bool Config::PrintConfig(char const * const configfilename,bool everything) const {
00701     char temp[50];char helpline[256];
00702     FILE* outfile=fopen(configfilename,"w+t");
00703     if(outfile==NULL) return false;
00704 
00705     /* Print start of configfile and add an return to improve readibility. */
00706     fprintf(outfile,MSG_Get("CONFIGFILE_INTRO"),VERSION);
00707     fprintf(outfile,"\n");
00708     for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){
00709         /* Print out the Section header */
00710         Section_prop *sec = dynamic_cast<Section_prop *>(*tel);
00711         strcpy(temp,(*tel)->GetName());
00712         lowcase(temp);
00713 
00714         if (sec) {
00715             int mods=0;
00716             Property *p;
00717             size_t i = 0, maxwidth = 0;
00718             while ((p = sec->Get_prop(i++))) {
00719                 if (!everything && !p->modified()) continue;
00720 
00721                 size_t w = strlen(p->propname.c_str());
00722                 if (w > maxwidth) maxwidth = w;
00723                 mods++;
00724             }
00725 
00726             if (!everything && mods == 0) {
00727                 /* nothing to print */
00728                 continue;
00729             }
00730 
00731             fprintf(outfile,"[%s]\n",temp);
00732 
00733             i=0;
00734             char prefix[80];
00735             snprintf(prefix,80, "\n# %*s  ", (int)maxwidth, "");
00736             while ((p = sec->Get_prop(i++))) {
00737                 if (!everything && !p->modified()) continue;
00738 
00739                 std::string help = p->Get_help();
00740                 std::string::size_type pos = std::string::npos;
00741                 while ((pos = help.find("\n", pos+1)) != std::string::npos) {
00742                     help.replace(pos, 1, prefix);
00743                 }
00744 
00745                 std::vector<Value> values = p->GetValues();
00746 
00747                 if (help != "" || !values.empty()) {
00748                     fprintf(outfile, "# %*s: %s", (int)maxwidth, p->propname.c_str(), help.c_str());
00749 
00750                     if (!values.empty()) {
00751                         fprintf(outfile, "%s%s:", prefix, MSG_Get("CONFIG_SUGGESTED_VALUES"));
00752                         std::vector<Value>::iterator it = values.begin();
00753                         while (it != values.end()) {
00754                             if((*it).ToString() != "%u") { //Hack hack hack. else we need to modify GetValues, but that one is const...
00755                                 if (it != values.begin()) fputs(",", outfile);
00756                                 fprintf(outfile, " %s", (*it).ToString().c_str());
00757                             }
00758                             ++it;
00759                         }
00760                         fprintf(outfile,".");
00761                     }
00762                     fprintf(outfile, "\n");
00763                 }
00764             }
00765         } else {
00766             fprintf(outfile,"[%s]\n",temp);
00767 
00768             upcase(temp);
00769             strcat(temp,"_CONFIGFILE_HELP");
00770             const char * helpstr=MSG_Get(temp);
00771             char * helpwrite=helpline;
00772             while (*helpstr) {
00773                 *helpwrite++=*helpstr;
00774                 if (*helpstr == '\n') {
00775                     *helpwrite=0;
00776                     fprintf(outfile,"# %s",helpline);
00777                     helpwrite=helpline;
00778                 }
00779                 helpstr++;
00780             }
00781         }
00782        
00783         (*tel)->PrintData(outfile,everything);
00784         fprintf(outfile,"\n");      /* Always an empty line between sections */
00785     }
00786     fclose(outfile);
00787     return true;
00788 }
00789    
00790 
00791 Section_prop* Config::AddSection_prop(char const * const _name,void (*_initfunction)(Section*),bool canchange){
00792     (void)_initfunction;//UNUSED
00793     (void)canchange;//UNUSED
00794     Section_prop* blah = new Section_prop(_name);
00795     sectionlist.push_back(blah);
00796     return blah;
00797 }
00798 
00799 Section_prop::~Section_prop() {
00800     /* Delete properties themself (properties stores the pointer of a prop */
00801     for(it prop = properties.begin(); prop != properties.end(); prop++) delete (*prop);
00802     properties.clear();
00803 }
00804 
00805 
00806 Section_line* Config::AddSection_line(char const * const _name,void (*_initfunction)(Section*)){
00807     (void)_initfunction;//UNUSED
00808     Section_line* blah = new Section_line(_name);
00809     sectionlist.push_back(blah);
00810     return blah;
00811 }
00812 
00813 void Null_Init(Section *sec);
00814 
00815 void AddExitFunction(SectionFunction func,const char *name,bool canchange) {
00816     /* NTS: Add functions so that iterating front to back executes them in First In Last Out order. */
00817     exitfunctions.push_front(Function_wrapper(func,canchange,name));
00818 }
00819 
00820 void AddVMEventFunction(enum vm_event event,SectionFunction func,const char *name,bool canchange) {
00821     assert(event < VM_EVENT_MAX);
00822 
00823     /* NTS: First In First Out order */
00824     vm_event_functions[event].push_back(Function_wrapper(func,canchange,name));
00825 }
00826 
00827 const char *VM_EVENT_string[VM_EVENT_MAX] = {
00828     "Power On",             // 0
00829     "Reset",
00830     "Reset Complete",
00831     "BIOS Init",
00832     "BIOS Boot",
00833 
00834     "Guest OS Boot",            // 5
00835     "DOS Boot",
00836     "DOS Init, kernel ready",
00837     "DOS Init, CONFIG.SYS done",
00838     "DOS Init, shell ready",
00839 
00840     "DOS Init, AUTOEXEC.BAT done",      // 10
00841     "DOS Init, at promot",
00842     "DOS exit, begin",
00843     "DOS exit, kernel exit",
00844     "DOS exit, reboot begin",
00845 
00846     "DOS exit, kernel reboot exit",     // 15
00847     "DOS surprise reboot",
00848     "Save state",
00849     "Load state"
00850 };
00851 
00852 VMDispatchState vm_dispatch_state;
00853 
00854 const char *GetVMEventName(enum vm_event event) {
00855     if (event >= VM_EVENT_MAX) return "";
00856     return VM_EVENT_string[event];
00857 }
00858 
00859 void DispatchVMEvent(enum vm_event event) {
00860     assert(event < VM_EVENT_MAX);
00861 
00862     LOG(LOG_MISC,LOG_DEBUG)("Dispatching VM event %s",GetVMEventName(event));
00863 
00864     vm_dispatch_state.begin_event(event);
00865     for (std::list<Function_wrapper>::iterator i=vm_event_functions[event].begin();i!=vm_event_functions[event].end();i++) {
00866         LOG(LOG_MISC,LOG_DEBUG)("Calling event %s handler (%p) '%s'",GetVMEventName(event),(void*)((uintptr_t)((*i).function)),(*i).name.c_str());
00867         (*i).function(NULL);
00868     }
00869 
00870     vm_dispatch_state.end_event();
00871 }
00872 
00873 Config::~Config() {
00874     std::list<Section*>::iterator it; // FIXME: You guys do realize C++ STL provides reverse_iterator?
00875 
00876     while ((it=sectionlist.end()) != sectionlist.begin()) {
00877         it--;
00878         delete (*it);
00879         sectionlist.erase(it);
00880     }
00881 }
00882 
00883 Section* Config::GetSection(int index){
00884     for (it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){
00885         if (!index--) return (*tel);
00886     }
00887     return NULL;
00888 }
00889 //c_str() 2x
00890 Section* Config::GetSection(string const& _sectionname) const{
00891     for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){
00892         if (!strcasecmp((*tel)->GetName(),_sectionname.c_str())) return (*tel);
00893     }
00894     return NULL;
00895 }
00896 
00897 Section* Config::GetSectionFromProperty(char const * const prop) const{
00898     for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){
00899         if ((*tel)->GetPropValue(prop) != NO_SUCH_PROPERTY) return (*tel);
00900     }
00901     return NULL;
00902 }
00903 
00904 
00905 bool Config::ParseConfigFile(char const * const configfilename){
00906     LOG(LOG_MISC,LOG_DEBUG)("Attempting to load config file #%zu from %s",configfiles.size(),configfilename);
00907 
00908     //static bool first_configfile = true;
00909     ifstream in(configfilename);
00910     if (!in) return false;
00911     const char * settings_type;
00912     settings_type = (configfiles.size() == 0)? "primary":"additional";
00913     configfiles.push_back(configfilename);
00914     
00915     LOG(LOG_MISC,LOG_NORMAL)("Loading %s settings from config file %s", settings_type,configfilename);
00916 
00917     //Get directory from configfilename, used with relative paths.
00918     current_config_dir=configfilename;
00919     std::string::size_type pos = current_config_dir.rfind(CROSS_FILESPLIT);
00920     if(pos == std::string::npos) pos = 0; //No directory then erase string
00921     current_config_dir.erase(pos);
00922 
00923     string gegevens;
00924     Section* currentsection = NULL;
00925     Section* testsec = NULL;
00926     while (getline(in,gegevens)) {
00927         
00928         /* strip leading/trailing whitespace */
00929         trim(gegevens);
00930         if(!gegevens.size()) continue;
00931 
00932         switch(gegevens[0]){
00933         case '%':
00934         case '\0':
00935         case '#':
00936         case ' ':
00937         case '\n':
00938             continue;
00939             break;
00940         case '[':
00941         {
00942             string::size_type loc = gegevens.find(']');
00943             if(loc == string::npos) continue;
00944             gegevens.erase(loc);
00945             testsec = GetSection(gegevens.substr(1));
00946             currentsection = testsec; /* NTS: If we don't recognize the section we WANT currentsection == NULL so it has no effect */
00947             testsec = NULL;
00948         }
00949             break;
00950         default:
00951             try {
00952                 if(currentsection) currentsection->HandleInputline(gegevens);
00953             } catch(const char* message) {
00954                 message=0;
00955                 //EXIT with message
00956             }
00957             break;
00958         }
00959     }
00960     current_config_dir.clear();//So internal changes don't use the path information
00961     return true;
00962 }
00963 
00964 void Config::ParseEnv(char ** envp) {
00965     for(char** env=envp; *env;env++) {
00966         char copy[1024];
00967         safe_strncpy(copy,*env,1024);
00968         if(strncasecmp(copy,"DOSBOX_",7))
00969             continue;
00970         char* sec_name = &copy[7];
00971         if(!(*sec_name))
00972             continue;
00973         char* prop_name = strrchr(sec_name,'_');
00974         if(!prop_name || !(*prop_name))
00975             continue;
00976         *prop_name++=0;
00977         Section* sect = GetSection(sec_name);
00978         if(!sect)
00979             continue;
00980         sect->HandleInputline(prop_name);
00981     }
00982 }
00983 
00984 bool CommandLine::FindExist(char const * const name,bool remove) {
00985     cmd_it it;
00986     if (!(FindEntry(name,it,false))) return false;
00987     if (remove) cmds.erase(it);
00988     return true;
00989 }
00990 
00991 bool CommandLine::FindHex(char const * const name,int & value,bool remove) {
00992     cmd_it it,it_next;
00993     if (!(FindEntry(name,it,true))) return false;
00994     it_next=it;it_next++;
00995     sscanf((*it_next).c_str(),"%X",(unsigned int*)(&value));
00996     if (remove) cmds.erase(it,++it_next);
00997     return true;
00998 }
00999 
01000 bool CommandLine::FindInt(char const * const name,int & value,bool remove) {
01001     cmd_it it,it_next;
01002     if (!(FindEntry(name,it,true))) return false;
01003     it_next=it;it_next++;
01004     value=atoi((*it_next).c_str());
01005     if (remove) cmds.erase(it,++it_next);
01006     return true;
01007 }
01008 
01009 bool CommandLine::FindString(char const * const name,std::string & value,bool remove) {
01010     cmd_it it,it_next;
01011     if (!(FindEntry(name,it,true))) return false;
01012     it_next=it;it_next++;
01013     value=*it_next;
01014     if (remove) cmds.erase(it,++it_next);
01015     return true;
01016 }
01017 
01018 bool CommandLine::FindCommand(unsigned int which,std::string & value) {
01019     if (which<1) return false;
01020     if (which>cmds.size()) return false;
01021     cmd_it it=cmds.begin();
01022     for (;which>1;which--) it++;
01023     value=(*it);
01024     return true;
01025 }
01026 
01027 bool CommandLine::FindEntry(char const * const name,cmd_it & it,bool neednext) {
01028     for (it=cmds.begin();it!=cmds.end();it++) {
01029         const char *d = (*it).c_str();
01030 
01031         /* HACK: If the search string starts with -, it's a switch,
01032          *       so adjust pointers so that it matches --switch or -switch */
01033         if (*name == '-' && d[0] == '-' && d[1] == '-') d++;
01034 
01035         if (!strcasecmp(d,name)) {
01036             cmd_it itnext=it;itnext++;
01037             if (neednext && (itnext==cmds.end())) return false;
01038             return true;
01039         }
01040     }
01041     return false;
01042 }
01043 
01044 bool CommandLine::FindStringBegin(char const* const begin,std::string & value, bool remove) {
01045     size_t len = strlen(begin);
01046     for (cmd_it it=cmds.begin();it!=cmds.end();it++) {
01047         if (strncmp(begin,(*it).c_str(),len)==0) {
01048             value=((*it).c_str() + len);
01049             if (remove) cmds.erase(it);
01050             return true;
01051         }
01052     }
01053     return false;
01054 }
01055 
01056 bool CommandLine::FindStringRemain(char const * const name,std::string & value) {
01057     cmd_it it;value="";
01058     if (!FindEntry(name,it)) return false;
01059     it++;
01060     for (;it!=cmds.end();it++) {
01061         value+=" ";
01062         value+=(*it);
01063     }
01064     return true;
01065 }
01066 
01067 /* Only used for parsing command.com /C 
01068  * Allowing /C dir and /Cdir
01069  * Restoring quotes back into the commands so command /C mount d "/tmp/a b" works as intended
01070  */
01071 bool CommandLine::FindStringRemainBegin(char const * const name,std::string & value) {
01072     cmd_it it;value="";
01073     if (!FindEntry(name,it)) {
01074         size_t len = strlen(name);
01075             for (it=cmds.begin();it!=cmds.end();it++) {
01076                 if (strncasecmp(name,(*it).c_str(),len)==0) {
01077                     std::string temp = ((*it).c_str() + len);
01078                     //Restore quotes for correct parsing in later stages
01079                     if(temp.find(" ") != std::string::npos)
01080                         value = std::string("\"") + temp + std::string("\"");
01081                     else
01082                         value = temp;
01083                     break;
01084                 }
01085             }
01086         if( it == cmds.end()) return false;
01087     }
01088     it++;
01089     for (;it!=cmds.end();it++) {
01090         value += " ";
01091         std::string temp = (*it);
01092         if(temp.find(" ") != std::string::npos)
01093             value += std::string("\"") + temp + std::string("\"");
01094         else
01095             value += temp;
01096     }
01097     return true;
01098 }
01099 
01100 bool CommandLine::GetStringRemain(std::string & value) {
01101     if(!cmds.size()) return false;
01102         
01103     cmd_it it=cmds.begin();value=(*it++);
01104     for(;it != cmds.end();it++) {
01105         value+=" ";
01106         value+=(*it);
01107     }
01108     return true;
01109 }
01110         
01111 
01112 unsigned int CommandLine::GetCount(void) {
01113     return (unsigned int)cmds.size();
01114 }
01115 
01116 bool CommandLine::GetCurrentArgv(std::string &argv) {
01117     if (opt_scan != cmds.end())
01118         argv = *opt_scan;
01119 
01120     return false;
01121 }
01122 
01123 bool CommandLine::CurrentArgvEnd(void) {
01124     return (opt_scan == cmds.end());
01125 }
01126 
01127 void CommandLine::EatCurrentArgv(void) {
01128     if (opt_scan != cmds.end()) opt_scan = cmds.erase(opt_scan);
01129 }
01130 
01131 void CommandLine::NextArgv(void) {
01132     if (opt_scan != cmds.end()) opt_scan++;
01133 }
01134 
01135 bool CommandLine::NextOptArgv(std::string &argv) {
01136     argv.clear();
01137 
01138     /* no argv to return if we're doing single-char GNU switches */
01139     if (!opt_gnu_getopt_singlechar.empty()) return false;
01140 
01141     if (opt_scan == cmds.end()) return false;
01142     argv = *opt_scan;
01143     if (opt_eat_argv) opt_scan = cmds.erase(opt_scan);
01144     else opt_scan++;
01145     return true;
01146 }
01147 
01148 void CommandLine::ChangeOptStyle(enum opt_style opt_style) {
01149     this->opt_style = opt_style;
01150 }
01151 
01152 bool CommandLine::BeginOpt(bool eat_argv) {
01153     opt_gnu_getopt_singlechar.clear();
01154     opt_scan = cmds.begin();
01155     if (opt_scan == cmds.end()) return false;
01156     opt_eat_argv = eat_argv;
01157     return true;
01158 }
01159 
01160 bool CommandLine::GetOptGNUSingleCharCheck(std::string &name) {
01161     char c;
01162 
01163     /* return another char, skipping spaces or invalid chars */
01164     name.clear();
01165     while (!opt_gnu_getopt_singlechar.empty()) {
01166         c = opt_gnu_getopt_singlechar.at(0);
01167         opt_gnu_getopt_singlechar = opt_gnu_getopt_singlechar.substr(1);
01168         if (c <= ' ' || c > 126) continue;
01169 
01170         name = c;
01171         return true;
01172     }
01173 
01174     return false;
01175 }
01176 
01177 bool CommandLine::GetOpt(std::string &name) {
01178     name.clear();
01179 
01180     /* if we're still doing GNU getopt single-char switches, then parse another and return */
01181     if (GetOptGNUSingleCharCheck(name))
01182         return true;
01183 
01184     while (opt_scan != cmds.end()) {
01185         std::string &argv = *opt_scan;
01186         const char *str = argv.c_str();
01187 
01188         if ((opt_style == CommandLine::either || opt_style == CommandLine::dos) && *str == '/') {
01189             /* MS-DOS style /option. Example: /A /OPT /HAX /BLAH */
01190             name = str+1; /* copy to caller minus leaking slash, then erase/skip */
01191             if (opt_eat_argv) opt_scan = cmds.erase(opt_scan);
01192             else opt_scan++;
01193             return true;
01194         }
01195         else if ((opt_style == CommandLine::either || opt_style == CommandLine::gnu || opt_style == CommandLine::gnu_getopt) && *str == '-') {
01196             str++; /* step past '-' */
01197             if (str[0] == '-' && str[1] == 0) { /* '--' means to stop parsing */
01198                 opt_scan = cmds.end();
01199                 if (opt_eat_argv) opt_scan = cmds.erase(opt_scan);
01200                 break;
01201             }
01202 
01203             if (opt_style == CommandLine::gnu_getopt) {
01204                 /* --switch => "switch"
01205                  * -switch => -s -w -i -t -c -h */
01206                 if (*str == '-') {
01207                     str++;
01208                 }
01209                 else {
01210                     /* assign to single-char parse then eat the argv */
01211                     opt_gnu_getopt_singlechar = str;
01212                     if (opt_eat_argv) opt_scan = cmds.erase(opt_scan);
01213                     else opt_scan++;
01214 
01215                     /* if we parse a single-char switch, great */
01216                     if (GetOptGNUSingleCharCheck(name))
01217                         return true;
01218 
01219                     continue; /* if we're here then there was nothing to parse, continue */
01220                 }
01221             }
01222             else {
01223                 /* -switch and --switch mean the same thing */
01224                 if (*str == '-') str++;
01225             }
01226 
01227             name = str; /* copy to caller, then erase/skip */
01228             if (opt_eat_argv) opt_scan = cmds.erase(opt_scan);
01229             else opt_scan++;
01230             return true;
01231         }
01232         else {
01233             opt_scan++;
01234         }
01235     }
01236 
01237     return false;
01238 }
01239 
01240 void CommandLine::EndOpt() {
01241     opt_scan = cmds.end();
01242 }
01243 
01244 void CommandLine::FillVector(std::vector<std::string> & vector) {
01245     for(cmd_it it=cmds.begin(); it != cmds.end(); it++) {
01246         vector.push_back((*it));
01247     }
01248     // add back the \" if the parameter contained a space
01249     for(Bitu i = 0; i < vector.size(); i++) {
01250         if(vector[i].find(' ') != std::string::npos) {
01251             vector[i] = "\""+vector[i]+"\"";
01252         }
01253     }
01254 }
01255 
01256 int CommandLine::GetParameterFromList(const char* const params[], std::vector<std::string> & output) {
01257     // return values: 0 = P_NOMATCH, 1 = P_NOPARAMS
01258     // TODO return nomoreparams
01259     int retval = 1;
01260     output.clear();
01261     enum {
01262         P_START, P_FIRSTNOMATCH, P_FIRSTMATCH
01263     } parsestate = P_START;
01264     cmd_it it = cmds.begin();
01265     while(it!=cmds.end()) {
01266         bool found = false;
01267         for(Bitu i = 0; params[i] != NULL; i++) {
01268             if (*params[i] == 0) {
01269                 LOG_MSG("FIXME: GetParameterFromList: terminating params[] with \"\" is deprecated. Please terminate the param list with NULL");
01270                 break;
01271             }
01272 
01273             if (!strcasecmp((*it).c_str(),params[i])) {
01274                 // found a parameter
01275                 found = true;
01276                 switch(parsestate) {
01277                 case P_START:
01278                     retval = i+2;
01279                     parsestate = P_FIRSTMATCH;
01280                     break;
01281                 case P_FIRSTMATCH:
01282                 case P_FIRSTNOMATCH:
01283                     return retval;
01284                 }
01285             }
01286         }
01287         if(!found) 
01288             switch(parsestate) {
01289             case P_START:
01290                 retval = 0; // no match
01291                 parsestate = P_FIRSTNOMATCH;
01292                 output.push_back(*it);
01293                 break;
01294             case P_FIRSTMATCH:
01295             case P_FIRSTNOMATCH:
01296                 output.push_back(*it);
01297                 break;
01298             }
01299         cmd_it itold = it;
01300         it++;
01301         cmds.erase(itold);
01302 
01303     }
01304     
01305     return retval;
01306 }
01307 
01308 
01309 CommandLine::CommandLine(int argc,char const * const argv[],enum opt_style opt) {
01310     if (argc>0) {
01311         file_name=argv[0];
01312     }
01313     int i=1;
01314     opt_style = opt;
01315     while (i<argc) {
01316         cmds.push_back(argv[i]);
01317         i++;
01318     }
01319 }
01320 
01321 Bit16u CommandLine::Get_arglength() {
01322     if(cmds.empty()) return 0;
01323     Bit16u i=1;
01324     for(cmd_it it=cmds.begin();it != cmds.end();it++) 
01325         i+=(*it).size() + 1;
01326     return --i;
01327 }
01328 
01329 
01330 CommandLine::CommandLine(char const * const name,char const * const cmdline,enum opt_style opt) {
01331     if (name) file_name=name;
01332     /* Parse the cmds and put them in the list */
01333     bool inword,inquote;char c;
01334     inword=false;inquote=false;
01335     std::string str;
01336     const char * c_cmdline=cmdline;
01337     opt_style = opt;
01338     while ((c=*c_cmdline)!=0) {
01339         if (inquote) {
01340             if (c!='"') str+=c;
01341             else {
01342                 inquote=false;
01343                 cmds.push_back(str);
01344                 str.erase();
01345             }
01346         }else if (inword) {
01347             if (c!=' ') str+=c;
01348             else {
01349                 inword=false;
01350                 cmds.push_back(str);
01351                 str.erase();
01352             }
01353         } 
01354         else if (c=='"') { inquote=true;}
01355         else if (c!=' ') { str+=c;inword=true;}
01356         c_cmdline++;
01357     }
01358     if (inword || inquote) cmds.push_back(str);
01359 }
01360 
01361 void CommandLine::Shift(unsigned int amount) {
01362     while(amount--) {
01363         file_name = cmds.size()?(*(cmds.begin())):"";
01364         if(cmds.size()) cmds.erase(cmds.begin());
01365     }
01366 }