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