DOSBox-X
|
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 = ©[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 }