1 /*
2  *  Copyright (C) 2002-2021  The DOSBox Team
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "setup.h"
20 
21 #include <algorithm>
22 #include <climits>
23 #include <cstdlib>
24 #include <fstream>
25 #include <limits>
26 #include <regex>
27 #include <sstream>
28 #include <string_view>
29 
30 #include "control.h"
31 #include "string_utils.h"
32 #include "support.h"
33 #include "cross.h"
34 
35 #if defined(_MSC_VER) || (defined(__MINGW32__) && defined(__clang__))
36 _CRTIMP extern char **_environ;
37 #else
38 extern char **environ;
39 #endif
40 
41 using namespace std;
42 static std::string current_config_dir; // Set by parseconfigfile so Prop_path can use it to construct the realpath
destroy()43 void Value::destroy() throw(){
44 	if (type == V_STRING) delete _string;
45 }
46 
copy(Value const & in)47 Value& Value::copy(Value const& in) {
48 	if (this != &in) { //Selfassigment!
49 		if(type != V_NONE && type != in.type) throw WrongType();
50 		destroy();
51 		plaincopy(in);
52 	}
53 	return *this;
54 }
55 
plaincopy(Value const & in)56 void Value::plaincopy(Value const& in) throw(){
57 	type = in.type;
58 	_int = in._int;
59 	_double = in._double;
60 	_bool = in._bool;
61 	_hex = in._hex;
62 	if(type == V_STRING) _string = new string(*in._string);
63 }
64 
operator bool() const65 Value::operator bool () const {
66 	if(type != V_BOOL) throw WrongType();
67 	return _bool;
68 }
69 
operator Hex() const70 Value::operator Hex () const {
71 	if(type != V_HEX) throw WrongType();
72 	return _hex;
73 }
74 
operator int() const75 Value::operator int () const {
76 	if(type != V_INT) throw WrongType();
77 	return _int;
78 }
79 
operator double() const80 Value::operator double () const {
81 	if(type != V_DOUBLE) throw WrongType();
82 	return _double;
83 }
84 
operator char const*() const85 Value::operator char const* () const {
86 	if(type != V_STRING) throw WrongType();
87 	return _string->c_str();
88 }
89 
operator ==(Value const & other) const90 bool Value::operator==(Value const& other) const {
91 	if(this == &other) return true;
92 	if(type != other.type) return false;
93 	switch(type){
94 		case V_BOOL:
95 			if(_bool == other._bool) return true;
96 			break;
97 		case V_INT:
98 			if(_int == other._int) return true;
99 			break;
100 		case V_HEX:
101 			if(_hex == other._hex) return true;
102 			break;
103 		case V_DOUBLE:
104 			if(_double == other._double) return true;
105 			break;
106 		case V_STRING:
107 			if((*_string) == (*other._string)) return true;
108 			break;
109 		default:
110 			E_Exit("comparing stuff that doesn't make sense");
111 			break;
112 	}
113 	return false;
114 }
SetValue(string const & in,Etype _type)115 bool Value::SetValue(string const& in,Etype _type) {
116 	/* Throw exception if the current type isn't the wanted type
117 	 * Unless the wanted type is current.
118 	 */
119 	if(_type == V_CURRENT && type == V_NONE) throw WrongType();
120 	if(_type != V_CURRENT) {
121 		if(type != V_NONE && type != _type) throw WrongType();
122 		type = _type;
123 	}
124 	bool retval = true;
125 	switch(type){
126 		case V_HEX:
127 			retval = set_hex(in);
128 			break;
129 		case V_INT:
130 			retval = set_int(in);
131 			break;
132 		case V_BOOL:
133 			retval = set_bool(in);
134 			break;
135 		case V_STRING:
136 			set_string(in);
137 			break;
138 		case V_DOUBLE:
139 			retval = set_double(in);
140 			break;
141 
142 		case V_NONE:
143 		case V_CURRENT:
144 		default:
145 			/* Shouldn't happen!/Unhandled */
146 			throw WrongType();
147 			break;
148 	}
149 	return retval;
150 }
151 
set_hex(std::string const & in)152 bool Value::set_hex(std::string const& in) {
153 	istringstream input(in);
154 	input.flags(ios::hex);
155 	int result = INT_MIN;
156 	input >> result;
157 	if(result == INT_MIN) return false;
158 	_hex = result;
159 	return true;
160 }
161 
set_int(string const & in)162 bool Value::set_int(string const &in) {
163 	istringstream input(in);
164 	int result = INT_MIN;
165 	input >> result;
166 	if(result == INT_MIN) return false;
167 	_int = result;
168 	return true;
169 }
set_double(string const & in)170 bool Value::set_double(string const &in) {
171 	istringstream input(in);
172 	double result = std::numeric_limits<double>::infinity();
173 	input >> result;
174 	if(result == std::numeric_limits<double>::infinity()) return false;
175 	_double = result;
176 	return true;
177 }
178 
set_bool(string const & in)179 bool Value::set_bool(string const &in) {
180 	istringstream input(in);
181 	string result;
182 	input >> result;
183 	lowcase(result);
184 	_bool = true; // TODO
185 	if(!result.size()) return false;
186 
187 	if(result=="0" || result=="disabled" || result=="false" || result=="off") {
188 		_bool = false;
189 	} else if(result=="1" || result=="enabled" || result=="true" || result=="on") {
190 		_bool = true;
191 	} else return false;
192 
193 	return true;
194 }
195 
set_string(string const & in)196 void Value::set_string(string const & in) {
197 	if(!_string) _string = new string();
198 	_string->assign(in);
199 }
200 
ToString() const201 string Value::ToString() const {
202 	ostringstream oss;
203 	switch(type) {
204 		case V_HEX:
205 			oss.flags(ios::hex);
206 			oss << _hex;
207 			break;
208 		case V_INT:
209 			oss << _int;
210 			break;
211 		case V_BOOL:
212 			oss << boolalpha << _bool;
213 			break;
214 		case V_STRING:
215 			oss << *_string;
216 			break;
217 		case V_DOUBLE:
218 			oss.precision(2);
219 			oss << fixed << _double;
220 			break;
221 		case V_NONE:
222 		case V_CURRENT:
223 		default:
224 			E_Exit("ToString messed up ?");
225 			break;
226 	}
227 	return oss.str();
228 }
229 
Property(const std::string & name,Changeable::Value when)230 Property::Property(const std::string &name, Changeable::Value when)
231         : propname(name),
232           value(),
233           suggested_values{},
234           default_value(),
235           change(when)
236 {
237 	assertm(std::regex_match(name, std::regex{"[a-zA-Z0-9_]+"}),
238 	        "Only letters, digits, and underscores are allowed in property name");
239 }
240 
CheckValue(Value const & in,bool warn)241 bool Property::CheckValue(Value const& in, bool warn){
242 	if (suggested_values.empty()) return true;
243 	for(const_iter it = suggested_values.begin();it != suggested_values.end();++it) {
244 		if ( (*it) == in) { //Match!
245 			return true;
246 		}
247 	}
248 	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());
249 	return false;
250 }
251 
Set_help(string const & in)252 void Property::Set_help(string const& in) {
253 	string result = string("CONFIG_") + propname;
254 	upcase(result);
255 	MSG_Add(result.c_str(),in.c_str());
256 }
257 
GetHelp() const258 const char * Property::GetHelp() const
259 {
260 	std::string result = "CONFIG_" + propname;
261 	upcase(result);
262 	return MSG_Get(result.c_str());
263 }
264 
SetVal(Value const & in,bool forced,bool warn)265 bool Prop_int::SetVal(Value const& in, bool forced, bool warn) {
266 	if (forced) {
267 		value = in;
268 		return true;
269 	} else if (!suggested_values.empty()){
270 		if ( CheckValue(in,warn) ) {
271 			value = in;
272 			return true;
273 		} else {
274 			value = default_value;
275 			return false;
276 		}
277 	} else {
278 		// Handle ranges if specified
279 		const int mi = min_value;
280 		const int ma = max_value;
281 		int va = static_cast<int>(Value(in));
282 
283 		//No ranges
284 		if (mi == -1 && ma == -1) { value = in; return true;}
285 
286 		//Inside range
287 		if (va >= mi && va <= ma) { value = in; return true;}
288 
289 		//Outside range, set it to the closest boundary
290 		if (va > ma ) va = ma; else va = mi;
291 
292 		if (warn) {
293 			LOG_MSG("%s is outside the allowed range %s-%s for variable: %s.\n"
294 			        "It has been set to the closest boundary: %d.",
295 			        in.ToString().c_str(),
296 			        min_value.ToString().c_str(),
297 			        max_value.ToString().c_str(),
298 			        propname.c_str(),
299 			        va);
300 		}
301 
302 		value = va;
303 		return true;
304 	}
305 }
CheckValue(Value const & in,bool warn)306 bool Prop_int::CheckValue(Value const& in, bool warn) {
307 //	if(!suggested_values.empty() && Property::CheckValue(in,warn)) return true;
308 	if(!suggested_values.empty()) return Property::CheckValue(in,warn);
309 	// LOG_MSG("still used ?");
310 	//No >= and <= in Value type and == is ambigious
311 	const int mi = min_value;
312 	const int ma = max_value;
313 	int va = static_cast<int>(Value(in));
314 	if (mi == -1 && ma == -1) return true;
315 	if (va >= mi && va <= ma) return true;
316 
317 	if (warn) {
318 		LOG_MSG("%s lies outside the range %s-%s for variable: %s.\n"
319 		        "It might now be reset to the default value: %s",
320 		        in.ToString().c_str(),
321 		        min_value.ToString().c_str(),
322 		        max_value.ToString().c_str(),
323 		        propname.c_str(),
324 		        default_value.ToString().c_str());
325 	}
326 	return false;
327 }
328 
SetValue(std::string const & input)329 bool Prop_double::SetValue(std::string const& input) {
330 	Value val;
331 	if(!val.SetValue(input,Value::V_DOUBLE)) return false;
332 	return SetVal(val,false,true);
333 }
334 
335 //void Property::SetValue(char* input){
336 //	value.SetValue(input, Value::V_CURRENT);
337 //}
SetValue(std::string const & input)338 bool Prop_int::SetValue(std::string const& input) {
339 	Value val;
340 	if (!val.SetValue(input,Value::V_INT)) return false;
341 	bool retval = SetVal(val,false,true);
342 	return retval;
343 }
344 
SetValue(std::string const & input)345 bool Prop_string::SetValue(std::string const& input) {
346 	//Special version for lowcase stuff
347 	std::string temp(input);
348 	//suggested values always case insensitive.
349 	//If there are none then it can be paths and such which are case sensitive
350 	if (!suggested_values.empty()) lowcase(temp);
351 	Value val(temp,Value::V_STRING);
352 	return SetVal(val,false,true);
353 }
CheckValue(Value const & in,bool warn)354 bool Prop_string::CheckValue(Value const& in, bool warn) {
355 	if (suggested_values.empty()) return true;
356 	for (const auto &val : suggested_values) {
357 		if (val == in) { // Match!
358 			return true;
359 		}
360 		if (val.ToString() == "%u") {
361 			unsigned int v;
362 			if (sscanf(in.ToString().c_str(), "%u", &v) == 1) {
363 				return true;
364 			}
365 		}
366 	}
367 	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());
368 	return false;
369 }
370 
SetValue(std::string const & input)371 bool Prop_path::SetValue(std::string const& input) {
372 	//Special version to merge realpath with it
373 
374 	Value val(input,Value::V_STRING);
375 	bool retval = SetVal(val,false,true);
376 
377 	if (input.empty()) {
378 		realpath.clear();
379 		return false;
380 	}
381 	std::string workcopy(input);
382 	Cross::ResolveHomedir(workcopy); //Parse ~ and friends
383 	//Prepend config directory in it exists. Check for absolute paths later
384 	if ( current_config_dir.empty()) realpath = workcopy;
385 	else realpath = current_config_dir + CROSS_FILESPLIT + workcopy;
386 	//Absolute paths
387 	if (Cross::IsPathAbsolute(workcopy)) realpath = workcopy;
388 	return retval;
389 }
390 
SetValue(std::string const & input)391 bool Prop_bool::SetValue(std::string const& input) {
392 	return value.SetValue(input,Value::V_BOOL);
393 }
394 
SetValue(std::string const & input)395 bool Prop_hex::SetValue(std::string const& input) {
396 	Value val;
397 	val.SetValue(input,Value::V_HEX);
398 	return SetVal(val,false,true);
399 }
400 
make_default_value()401 void Prop_multival::make_default_value()
402 {
403 	Property *p = section->Get_prop(0);
404 	if (!p) return;
405 
406 	int i = 1;
407 	std::string result = p->Get_Default_Value().ToString();
408 	while( (p = section->Get_prop(i++)) ) {
409 		std::string props = p->Get_Default_Value().ToString();
410 		if (props.empty()) continue;
411 		result += separator; result += props;
412 	}
413 	Value val(result,Value::V_STRING);
414 	SetVal(val,false,true);
415 }
416 
417 //TODO checkvalue stuff
SetValue(std::string const & input)418 bool Prop_multival_remain::SetValue(std::string const& input) {
419 	Value val(input,Value::V_STRING);
420 	bool retval = SetVal(val,false,true);
421 
422 	std::string local(input);
423 	int i = 0,number_of_properties = 0;
424 	Property *p = section->Get_prop(0);
425 	//No properties in this section. do nothing
426 	if (!p) return false;
427 
428 	while( (section->Get_prop(number_of_properties)) )
429 		number_of_properties++;
430 
431 	string::size_type loc = string::npos;
432 	while( (p = section->Get_prop(i++)) ) {
433 		//trim leading separators
434 		loc = local.find_first_not_of(separator);
435 		if (loc != string::npos) local.erase(0,loc);
436 		loc = local.find_first_of(separator);
437 		string in = "";//default value
438 		/* when i == number_of_properties add the total line. (makes more then
439 		 * one string argument possible for parameters of cpu) */
440 		if (loc != string::npos && i < number_of_properties) { //separator found
441 			in = local.substr(0,loc);
442 			local.erase(0,loc+1);
443 		} else if (local.size()) { //last argument or last property
444 			in = local;
445 			local.clear();
446 		}
447 		//Test Value. If it fails set default
448 		Value valtest (in,p->Get_type());
449 		if (!p->CheckValue(valtest,true)) {
450 			make_default_value();
451 			return false;
452 		}
453 		p->SetValue(in);
454 	}
455 	return retval;
456 }
457 
458 //TODO checkvalue stuff
SetValue(std::string const & input)459 bool Prop_multival::SetValue(std::string const& input) {
460 	Value val(input,Value::V_STRING);
461 	bool retval = SetVal(val,false,true);
462 
463 	std::string local(input);
464 	int i = 0;
465 	Property *p = section->Get_prop(0);
466 	//No properties in this section. do nothing
467 	if (!p) return false;
468 	Value::Etype prevtype = Value::V_NONE;
469 	string prevargument = "";
470 
471 	string::size_type loc = string::npos;
472 	while( (p = section->Get_prop(i++)) ) {
473 		//trim leading separators
474 		loc = local.find_first_not_of(separator);
475 		if (loc != string::npos) local.erase(0,loc);
476 		loc = local.find_first_of(separator);
477 		string in = "";//default value
478 		if (loc != string::npos) { //separator found
479 			in = local.substr(0,loc);
480 			local.erase(0,loc+1);
481 		} else if (local.size()) { //last argument
482 			in = local;
483 			local.clear();
484 		}
485 
486 		if (p->Get_type() == Value::V_STRING) {
487 			//Strings are only checked against the suggested values list.
488 			//Test Value. If it fails set default
489 			Value valtest (in,p->Get_type());
490 			if (!p->CheckValue(valtest,true)) {
491 				make_default_value();
492 				return false;
493 			}
494 			p->SetValue(in);
495 		} else {
496 			//Non-strings can have more things, conversion alone is not enough (as invalid values as converted to 0)
497 			bool r = p->SetValue(in);
498 			if (!r) {
499 				if (in.empty() && p->Get_type() == prevtype ) {
500 					//Nothing there, but same type of variable, so repeat it (sensitivity)
501 					in = prevargument;
502 					p->SetValue(in);
503 				} else {
504 					//Something was there to be parsed or not the same type. Invalidate entire property.
505 					make_default_value();
506 				}
507 			}
508 		}
509 		prevtype = p->Get_type();
510 		prevargument = in;
511 
512 	}
513 	return retval;
514 }
515 
GetValues() const516 const std::vector<Value>& Property::GetValues() const {
517 	return suggested_values;
518 }
GetValues() const519 const std::vector<Value>& Prop_multival::GetValues() const {
520 	Property *p = section->Get_prop(0);
521 	//No properties in this section. do nothing
522 	if (!p) return suggested_values;
523 	int i =0;
524 	while( (p = section->Get_prop(i++)) ) {
525 		std::vector<Value> v = p->GetValues();
526 		if(!v.empty()) return p->GetValues();
527 	}
528 	return suggested_values;
529 }
530 
531 /*
532 void Section_prop::Add_double(char const * const _propname, double _value) {
533 	Property* test=new Prop_double(_propname,_value);
534 	properties.push_back(test);
535 }*/
536 
Set_values(const char * const * in)537 void Property::Set_values(const char * const *in) {
538 	Value::Etype type = default_value.type;
539 	int i = 0;
540 	while (in[i]) {
541 		Value val(in[i],type);
542 		suggested_values.push_back(val);
543 		i++;
544 	}
545 }
546 
Set_values(const std::vector<std::string> & in)547 void Property::Set_values(const std::vector<std::string> & in) {
548 	Value::Etype type = default_value.type;
549 	for (auto &str : in) {
550 		Value val(str, type);
551 		suggested_values.push_back(val);
552 	}
553 }
554 
Add_int(string const & _propname,Property::Changeable::Value when,int _value)555 Prop_int* Section_prop::Add_int(string const& _propname, Property::Changeable::Value when, int _value) {
556 	Prop_int* test=new Prop_int(_propname,when,_value);
557 	properties.push_back(test);
558 	return test;
559 }
560 
Add_string(string const & _propname,Property::Changeable::Value when,char const * const _value)561 Prop_string* Section_prop::Add_string(string const& _propname, Property::Changeable::Value when, char const * const _value) {
562 	Prop_string* test=new Prop_string(_propname,when,_value);
563 	properties.push_back(test);
564 	return test;
565 }
566 
Add_path(string const & _propname,Property::Changeable::Value when,char const * const _value)567 Prop_path* Section_prop::Add_path(string const& _propname, Property::Changeable::Value when, char const * const _value) {
568 	Prop_path* test=new Prop_path(_propname,when,_value);
569 	properties.push_back(test);
570 	return test;
571 }
572 
Add_bool(string const & _propname,Property::Changeable::Value when,bool _value)573 Prop_bool* Section_prop::Add_bool(string const& _propname, Property::Changeable::Value when, bool _value) {
574 	Prop_bool* test=new Prop_bool(_propname,when,_value);
575 	properties.push_back(test);
576 	return test;
577 }
578 
Add_hex(string const & _propname,Property::Changeable::Value when,Hex _value)579 Prop_hex* Section_prop::Add_hex(string const& _propname, Property::Changeable::Value when, Hex _value) {
580 	Prop_hex* test=new Prop_hex(_propname,when,_value);
581 	properties.push_back(test);
582 	return test;
583 }
584 
Add_multi(std::string const & _propname,Property::Changeable::Value when,std::string const & sep)585 Prop_multival* Section_prop::Add_multi(std::string const& _propname, Property::Changeable::Value when,std::string const& sep) {
586 	Prop_multival* test = new Prop_multival(_propname,when,sep);
587 	properties.push_back(test);
588 	return test;
589 }
590 
Add_multiremain(std::string const & _propname,Property::Changeable::Value when,std::string const & sep)591 Prop_multival_remain* Section_prop::Add_multiremain(std::string const& _propname, Property::Changeable::Value when,std::string const& sep) {
592 	Prop_multival_remain* test = new Prop_multival_remain(_propname,when,sep);
593 	properties.push_back(test);
594 	return test;
595 }
596 
Get_int(string const & _propname) const597 int Section_prop::Get_int(string const&_propname) const {
598 	for(const_it tel=properties.begin();tel!=properties.end();tel++){
599 		if ((*tel)->propname==_propname){
600 			return ((*tel)->GetValue());
601 		}
602 	}
603 	return 0;
604 }
605 
Get_bool(string const & _propname) const606 bool Section_prop::Get_bool(string const& _propname) const {
607 	for(const_it tel = properties.begin();tel != properties.end();++tel){
608 		if ((*tel)->propname == _propname){
609 			return ((*tel)->GetValue());
610 		}
611 	}
612 	return false;
613 }
614 
Get_double(string const & _propname) const615 double Section_prop::Get_double(string const& _propname) const {
616 	for(const_it tel = properties.begin();tel != properties.end();++tel){
617 		if ((*tel)->propname == _propname){
618 			return ((*tel)->GetValue());
619 		}
620 	}
621 	return 0.0;
622 }
623 
Get_path(string const & _propname) const624 Prop_path* Section_prop::Get_path(string const& _propname) const {
625 	for(const_it tel = properties.begin();tel != properties.end();++tel){
626 		if ((*tel)->propname == _propname){
627 			Prop_path* val = dynamic_cast<Prop_path*>((*tel));
628 			if (val) return val; else return NULL;
629 		}
630 	}
631 	return NULL;
632 }
633 
Get_multival(string const & _propname) const634 Prop_multival* Section_prop::Get_multival(string const& _propname) const {
635 	for(const_it tel = properties.begin();tel != properties.end();++tel){
636 		if ((*tel)->propname == _propname){
637 			Prop_multival* val = dynamic_cast<Prop_multival*>((*tel));
638 			if(val) return val; else return NULL;
639 		}
640 	}
641 	return NULL;
642 }
643 
Get_multivalremain(string const & _propname) const644 Prop_multival_remain* Section_prop::Get_multivalremain(string const& _propname) const {
645 	for(const_it tel = properties.begin();tel != properties.end();++tel){
646 		if ((*tel)->propname == _propname){
647 			Prop_multival_remain* val = dynamic_cast<Prop_multival_remain*>((*tel));
648 			if (val) return val; else return NULL;
649 		}
650 	}
651 	return NULL;
652 }
Get_prop(int index)653 Property* Section_prop::Get_prop(int index){
654 	for(it tel = properties.begin();tel != properties.end();++tel){
655 		if (!index--) return (*tel);
656 	}
657 	return NULL;
658 }
659 
Get_string(string const & _propname) const660 const char* Section_prop::Get_string(string const& _propname) const {
661 	for(const_it tel = properties.begin();tel != properties.end();++tel){
662 		if ((*tel)->propname == _propname){
663 			return ((*tel)->GetValue());
664 		}
665 	}
666 	return "";
667 }
Get_hex(string const & _propname) const668 Hex Section_prop::Get_hex(string const& _propname) const {
669 	for(const_it tel = properties.begin();tel != properties.end();++tel){
670 		if ((*tel)->propname == _propname){
671 			return ((*tel)->GetValue());
672 		}
673 	}
674 	return 0;
675 }
676 
HandleInputline(string const & gegevens)677 bool Section_prop::HandleInputline(string const& gegevens){
678 	string str1 = gegevens;
679 	string::size_type loc = str1.find('=');
680 	if (loc == string::npos) return false;
681 	string name = str1.substr(0,loc);
682 	string val = str1.substr(loc + 1);
683 
684 	/* Remove quotes around value */
685 	trim(val);
686 	string::size_type length = val.length();
687 	if (length > 1 &&
688 	     ((val[0] == '\"'  && val[length - 1] == '\"' ) ||
689 	      (val[0] == '\'' && val[length - 1] == '\''))
690 	   ) val = val.substr(1,length - 2);
691 
692 	/* trim the results incase there were spaces somewhere */
693 	trim(name);
694 	trim(val);
695 	for (auto &p : properties) {
696 
697 		if (strcasecmp(p->propname.c_str(), name.c_str()) != 0)
698 			continue;
699 
700 		if (p->IsDeprecated()) {
701 			LOG_MSG("CONFIG: Deprecated option '%s'", name.c_str());
702 			LOG_MSG("CONFIG: %s", p->GetHelp());
703 			return false;
704 		}
705 
706 		return p->SetValue(val);
707 	}
708 	LOG_MSG("CONFIG: Unknown option %s", name.c_str());
709 	return false;
710 }
711 
PrintData(FILE * outfile) const712 void Section_prop::PrintData(FILE* outfile) const {
713 	/* Now print out the individual section entries */
714 
715 	// Determine maximum length of the props in this section
716 	int len = 0;
717 	for (const auto &tel : properties) {
718 		const auto prop_length = check_cast<int>(tel->propname.length());
719 		len = std::max<int>(len, prop_length);
720 	}
721 
722 	for (const auto &tel : properties) {
723 
724 		if (tel->IsDeprecated())
725 			continue;
726 
727 		fprintf(outfile, "%-*s = %s\n",
728 		        std::min<int>(40, len),
729 		        tel->propname.c_str(),
730 		        tel->GetValue().ToString().c_str());
731 	}
732 }
733 
GetPropValue(string const & _property) const734 string Section_prop::GetPropValue(string const& _property) const {
735 	for(const_it tel = properties.begin();tel != properties.end();++tel){
736 		if (!strcasecmp((*tel)->propname.c_str(),_property.c_str())){
737 			return (*tel)->GetValue().ToString();
738 		}
739 	}
740 	return NO_SUCH_PROPERTY;
741 }
742 
HandleInputline(const std::string & line)743 bool Section_line::HandleInputline(const std::string &line)
744 {
745 	if (!data.empty()) data += "\n"; //Add return to previous line in buffer
746 	data += line;
747 	return true;
748 }
749 
PrintData(FILE * outfile) const750 void Section_line::PrintData(FILE *outfile) const
751 {
752 	fprintf(outfile, "%s", data.c_str());
753 }
754 
GetPropValue(const std::string &) const755 std::string Section_line::GetPropValue(const std::string &) const
756 {
757 	return NO_SUCH_PROPERTY;
758 }
759 
PrintConfig(const std::string & filename) const760 bool Config::PrintConfig(const std::string &filename) const
761 {
762 	char temp[50];
763 	char helpline[256];
764 	FILE *outfile = fopen(filename.c_str(), "w+t");
765 	if (outfile == NULL) return false;
766 
767 	/* Print start of configfile and add a return to improve readibility. */
768 	fprintf(outfile, MSG_Get("CONFIGFILE_INTRO"), VERSION);
769 	fprintf(outfile, "\n");
770 
771 	for (auto tel = sectionlist.cbegin(); tel != sectionlist.cend(); ++tel) {
772 		/* Print out the Section header */
773 		safe_strcpy(temp, (*tel)->GetName());
774 		lowcase(temp);
775 		fprintf(outfile,"[%s]\n",temp);
776 
777 		Section_prop *sec = dynamic_cast<Section_prop *>(*tel);
778 		if (sec) {
779 			Property *p;
780 			int i = 0;
781 			size_t maxwidth = 0;
782 			while ((p = sec->Get_prop(i++))) {
783 				maxwidth = std::max(maxwidth, p->propname.length());
784 			}
785 			i=0;
786 			char prefix[80];
787 			int intmaxwidth = std::min<int>(60, check_cast<int>(maxwidth));
788 			safe_sprintf(prefix, "\n# %*s  ", intmaxwidth, "");
789 			while ((p = sec->Get_prop(i++))) {
790 
791 				if (p->IsDeprecated())
792 					continue;
793 
794 				std::string help = p->GetHelp();
795 				std::string::size_type pos = std::string::npos;
796 				while ((pos = help.find('\n', pos+1)) != std::string::npos) {
797 					help.replace(pos, 1, prefix);
798 				}
799 
800 				fprintf(outfile, "# %*s: %s", intmaxwidth, p->propname.c_str(), help.c_str());
801 
802 				std::vector<Value> values = p->GetValues();
803 				if (!values.empty()) {
804 					fprintf(outfile, "%s%s:", prefix, MSG_Get("CONFIG_SUGGESTED_VALUES"));
805 					std::vector<Value>::const_iterator it = values.begin();
806 					while (it != values.end()) {
807 						if((*it).ToString() != "%u") { //Hack hack hack. else we need to modify GetValues, but that one is const...
808 							if (it != values.begin()) fputs(",", outfile);
809 							fprintf(outfile, " %s", (*it).ToString().c_str());
810 						}
811 						++it;
812 					}
813 					fprintf(outfile,".");
814 				}
815 			fprintf(outfile, "\n");
816 			}
817 		} else {
818 			upcase(temp);
819 			strcat(temp,"_CONFIGFILE_HELP");
820 			const char * helpstr = MSG_Get(temp);
821 			const char * linestart = helpstr;
822 			char * helpwrite = helpline;
823 			while (*helpstr && static_cast<size_t>(helpstr - linestart) < sizeof(helpline)) {
824 				*helpwrite++ = *helpstr;
825 				if (*helpstr == '\n') {
826 					*helpwrite = 0;
827 					fprintf(outfile, "# %s", helpline);
828 					helpwrite = helpline;
829 					linestart = ++helpstr;
830 				} else {
831 					++helpstr;
832 				}
833 			}
834 		}
835 
836 		fprintf(outfile,"\n");
837 		(*tel)->PrintData(outfile);
838 		fprintf(outfile,"\n");		/* Always an empty line between sections */
839 	}
840 	fclose(outfile);
841 	return true;
842 }
843 
AddEarlySectionProp(const char * name,SectionFunction func,bool changeable_at_runtime)844 Section_prop *Config::AddEarlySectionProp(const char *name,
845                                           SectionFunction func,
846                                           bool changeable_at_runtime)
847 {
848 	Section_prop *s = new Section_prop(name);
849 	s->AddEarlyInitFunction(func, changeable_at_runtime);
850 	sectionlist.push_back(s);
851 	return s;
852 }
853 
AddSection_prop(const char * section_name,SectionFunction func,bool changeable_at_runtime)854 Section_prop *Config::AddSection_prop(const char *section_name,
855                                       SectionFunction func,
856                                       bool changeable_at_runtime)
857 {
858 	assertm(std::regex_match(section_name, std::regex{"[a-zA-Z0-9]+"}),
859 	        "Only letters and digits are allowed in section name");
860 	Section_prop *s = new Section_prop(section_name);
861 	s->AddInitFunction(func, changeable_at_runtime);
862 	sectionlist.push_back(s);
863 	return s;
864 }
865 
~Section_prop()866 Section_prop::~Section_prop()
867 {
868 	//ExecuteDestroy should be here else the destroy functions use destroyed properties
869 	ExecuteDestroy(true);
870 	/* Delete properties themself (properties stores the pointer of a prop */
871 	for(it prop = properties.begin(); prop != properties.end(); ++prop)
872 		delete (*prop);
873 }
874 
AddSection_line(const char * section_name,SectionFunction func)875 Section_line *Config::AddSection_line(const char *section_name, SectionFunction func)
876 {
877 	assertm(std::regex_match(section_name, std::regex{"[a-zA-Z0-9]+"}),
878 	        "Only letters and digits are allowed in section name");
879 	Section_line *blah = new Section_line(section_name);
880 	blah->AddInitFunction(func);
881 	sectionlist.push_back(blah);
882 	return blah;
883 }
884 
Init() const885 void Config::Init() const
886 {
887 	for (const auto &sec : sectionlist)
888 		sec->ExecuteEarlyInit();
889 
890 	for (const auto &sec : sectionlist)
891 		sec->ExecuteInit();
892 }
893 
AddEarlyInitFunction(SectionFunction func,bool changeable_at_runtime)894 void Section::AddEarlyInitFunction(SectionFunction func, bool changeable_at_runtime)
895 {
896 	early_init_functions.emplace_back(func, changeable_at_runtime);
897 }
898 
AddInitFunction(SectionFunction func,bool changeable_at_runtime)899 void Section::AddInitFunction(SectionFunction func, bool changeable_at_runtime)
900 {
901 	initfunctions.emplace_back(func, changeable_at_runtime);
902 }
903 
AddDestroyFunction(SectionFunction func,bool changeable_at_runtime)904 void Section::AddDestroyFunction(SectionFunction func, bool changeable_at_runtime)
905 {
906 	destroyfunctions.emplace_front(func, changeable_at_runtime);
907 }
908 
ExecuteEarlyInit(bool init_all)909 void Section::ExecuteEarlyInit(bool init_all)
910 {
911 	for (const auto &fn : early_init_functions)
912 		if (init_all || fn.changeable_at_runtime)
913 			fn.function(this);
914 }
915 
ExecuteInit(bool initall)916 void Section::ExecuteInit(bool initall)
917 {
918 	for (const auto &fn : initfunctions)
919 		if (initall || fn.changeable_at_runtime)
920 			fn.function(this);
921 }
922 
ExecuteDestroy(bool destroyall)923 void Section::ExecuteDestroy(bool destroyall) {
924 	typedef std::deque<Function_wrapper>::iterator func_it;
925 	for (func_it tel = destroyfunctions.begin(); tel != destroyfunctions.end(); ) {
926 		if (destroyall || (*tel).changeable_at_runtime) {
927 			(*tel).function(this);
928 			tel = destroyfunctions.erase(tel); //Remove destroyfunction once used
929 		} else
930 			++tel;
931 	}
932 }
933 
~Config()934 Config::~Config()
935 {
936 	for (auto cnt = sectionlist.rbegin(); cnt != sectionlist.rend(); ++cnt)
937 		delete (*cnt);
938 }
939 
GetSection(const std::string & section_name) const940 Section *Config::GetSection(const std::string &section_name) const
941 {
942 	for (auto *el : sectionlist) {
943 		if (!strcasecmp(el->GetName(), section_name.c_str()))
944 			return el;
945 	}
946 	return nullptr;
947 }
948 
GetSectionFromProperty(const char * prop) const949 Section *Config::GetSectionFromProperty(const char *prop) const
950 {
951 	for (auto *el : sectionlist) {
952 		if (el->GetPropValue(prop) != NO_SUCH_PROPERTY)
953 			return el;
954 	}
955 	return nullptr;
956 }
957 
OverwriteAutoexec(const std::string & conf,const std::string & line)958 void Config::OverwriteAutoexec(const std::string &conf, const std::string &line)
959 {
960 	// If we're in a new config file, then record that filename and reset
961 	// the section
962 	if (overwritten_autoexec_conf != conf) {
963 		overwritten_autoexec_conf = conf;
964 		overwritten_autoexec_section.data.clear();
965 	}
966 	overwritten_autoexec_section.HandleInputline(line);
967 }
968 
GetOverwrittenAutoexecConf() const969 const std::string &Config::GetOverwrittenAutoexecConf() const
970 {
971 	return overwritten_autoexec_conf;
972 }
973 
GetOverwrittenAutoexecSection() const974 const Section_line &Config::GetOverwrittenAutoexecSection() const
975 {
976 	return overwritten_autoexec_section;
977 }
978 
ParseConfigFile(const std::string & type,const std::string & configfilename)979 bool Config::ParseConfigFile(const std::string &type, const std::string &configfilename)
980 {
981 	// static bool first_configfile = true;
982 	ifstream in(configfilename);
983 	if (!in)
984 		return false;
985 	configfiles.push_back(configfilename);
986 
987 	// Get directory from configfilename, used with relative paths.
988 	current_config_dir = configfilename;
989 	auto split_pos = current_config_dir.rfind(CROSS_FILESPLIT);
990 	if (split_pos == std::string::npos)
991 		split_pos = 0; // No directory then erase string
992 	current_config_dir.erase(split_pos);
993 
994 	string gegevens;
995 	Section *currentsection = nullptr;
996 
997 	while (getline(in, gegevens)) {
998 		/* strip leading/trailing whitespace */
999 		trim(gegevens);
1000 		if (gegevens.empty())
1001 			continue;
1002 
1003 		switch (gegevens[0]) {
1004 		case '%':
1005 		case '\0':
1006 		case '#':
1007 		case ' ':
1008 		case '\n': continue; break;
1009 		case '[': {
1010 			const auto bracket_pos = gegevens.find(']');
1011 			if (bracket_pos == string::npos)
1012 				continue;
1013 			gegevens.erase(bracket_pos);
1014 			const auto section_name = gegevens.substr(1);
1015 			if (const auto sec = GetSection(section_name); sec)
1016 				currentsection = sec;
1017 		} break;
1018 		default:
1019 			if (currentsection) {
1020 				currentsection->HandleInputline(gegevens);
1021 
1022 				// If this is an autoexec section, the above takes care of the joining
1023 				// while this handles the overwrriten mode.
1024 				// We need to be prepared for either scenario to play out because we
1025 				// won't know the users final preferance until the very last configuration
1026 				// file is processed.
1027 				if (std::string_view(currentsection->GetName()) == "autoexec") {
1028 					OverwriteAutoexec(configfilename, gegevens);
1029 				}
1030 			}
1031 			break;
1032 		}
1033 	}
1034 	current_config_dir.clear();//So internal changes don't use the path information
1035 
1036 	LOG_INFO("CONFIG: Loaded %s conf file %s", type.c_str(), configfilename.c_str());
1037 
1038 	return true;
1039 }
1040 
parse_environ(const char * const * envp)1041 parse_environ_result_t parse_environ(const char * const * envp) noexcept
1042 {
1043 	assert(envp);
1044 
1045 	// Filter envirnment variables in following format:
1046 	// DOSBOX_SECTIONNAME_PROPNAME=VALUE (prefix, section, and property
1047 	// names are case-insensitive).
1048 	std::list<std::tuple<std::string, std::string>> props_to_set;
1049 	for (const char * const *str = envp; *str; str++) {
1050 		const char *env_var = *str;
1051 		if (strncasecmp(env_var, "DOSBOX_", 7) != 0)
1052 			continue;
1053 		const std::string rest = (env_var + 7);
1054 		const auto section_delimiter = rest.find('_');
1055 		if (section_delimiter == string::npos)
1056 			continue;
1057 		const auto section_name = rest.substr(0, section_delimiter);
1058 		if (section_name.empty())
1059 			continue;
1060 		const auto prop_name_and_value = rest.substr(section_delimiter + 1);
1061 		if (prop_name_and_value.empty() || !isalpha(prop_name_and_value[0]))
1062 			continue;
1063 		props_to_set.emplace_back(std::make_tuple(section_name,
1064 		                                          prop_name_and_value));
1065 	}
1066 
1067 	return props_to_set;
1068 }
1069 
ParseEnv()1070 void Config::ParseEnv()
1071 {
1072 #if defined(_MSC_VER) || (defined(__MINGW32__) && defined(__clang__))
1073 	const char *const *envp = _environ;
1074 #else
1075 	const char *const *envp = environ;
1076 #endif
1077 	if (envp == nullptr)
1078 		return;
1079 
1080 	for (const auto &set_prop_desc : parse_environ(envp)) {
1081 		const auto section_name = std::get<0>(set_prop_desc);
1082 		Section *sec = GetSection(section_name);
1083 		if (!sec)
1084 			continue;
1085 		const auto prop_name_and_value = std::get<1>(set_prop_desc);
1086 		sec->HandleInputline(prop_name_and_value);
1087 	}
1088 }
1089 
SetStartUp(void (* _function)(void))1090 void Config::SetStartUp(void (*_function)(void)) {
1091 	_start_function=_function;
1092 }
1093 
StartUp()1094 void Config::StartUp()
1095 {
1096 	(*_start_function)();
1097 }
1098 
GetStartupVerbosity() const1099 Verbosity Config::GetStartupVerbosity() const
1100 {
1101 	const Section* s = GetSection("dosbox");
1102 	assert(s);
1103 	const std::string user_choice = s->GetPropValue("startup_verbosity");
1104 
1105 	if (user_choice == "high")
1106 		return Verbosity::High;
1107 	if (user_choice == "medium")
1108 		return Verbosity::Medium;
1109 	if (user_choice == "low")
1110 		return Verbosity::Low;
1111 	if (user_choice == "splash_only")
1112 		return Verbosity::SplashOnly;
1113 	if (user_choice == "quiet")
1114 		return Verbosity::Quiet;
1115 	if (user_choice == "auto")
1116 		return (cmdline->HasDirectory() || cmdline->HasExecutableName())
1117 		               ? Verbosity::InstantLaunch
1118 		               : Verbosity::High;
1119 
1120 	LOG_WARNING("SETUP: Unknown verbosity mode '%s', defaulting to 'high'",
1121 	            user_choice.c_str());
1122 	return Verbosity::High;
1123 }
1124 
FindExist(char const * const name,bool remove)1125 bool CommandLine::FindExist(char const * const name,bool remove) {
1126 	cmd_it it;
1127 	if (!(FindEntry(name,it,false))) return false;
1128 	if (remove) cmds.erase(it);
1129 	return true;
1130 }
1131 
FindInt(char const * const name,int & value,bool remove)1132 bool CommandLine::FindInt(char const * const name,int & value,bool remove) {
1133 	cmd_it it,it_next;
1134 	if (!(FindEntry(name,it,true))) return false;
1135 	it_next=it;++it_next;
1136 	value=atoi((*it_next).c_str());
1137 	if (remove) cmds.erase(it,++it_next);
1138 	return true;
1139 }
1140 
FindString(char const * const name,std::string & value,bool remove)1141 bool CommandLine::FindString(char const * const name,std::string & value,bool remove) {
1142 	cmd_it it,it_next;
1143 	if (!(FindEntry(name,it,true))) return false;
1144 	it_next=it;++it_next;
1145 	value=*it_next;
1146 	if (remove) cmds.erase(it,++it_next);
1147 	return true;
1148 }
1149 
FindCommand(unsigned int which,std::string & value)1150 bool CommandLine::FindCommand(unsigned int which,std::string & value) {
1151 	if (which<1) return false;
1152 	if (which>cmds.size()) return false;
1153 	cmd_it it=cmds.begin();
1154 	for (;which>1;which--) it++;
1155 	value=(*it);
1156 	return true;
1157 }
1158 
1159 // Was a directory provided on the command line?
HasDirectory() const1160 bool CommandLine::HasDirectory() const
1161 {
1162 	for (const auto& arg : cmds)
1163 		if (open_directory(arg.c_str()))
1164 			return true;
1165 	return false;
1166 }
1167 
1168 // Was an executable filename provided on the command line?
HasExecutableName() const1169 bool CommandLine::HasExecutableName() const
1170 {
1171 	for (const auto& arg : cmds)
1172 		if (is_executable_filename(arg))
1173 			return true;
1174 	return false;
1175 }
1176 
FindEntry(char const * const name,cmd_it & it,bool neednext)1177 bool CommandLine::FindEntry(char const * const name,cmd_it & it,bool neednext) {
1178 	for (it = cmds.begin(); it != cmds.end(); ++it) {
1179 		if (!strcasecmp((*it).c_str(),name)) {
1180 			cmd_it itnext=it;++itnext;
1181 			if (neednext && (itnext==cmds.end())) return false;
1182 			return true;
1183 		}
1184 	}
1185 	return false;
1186 }
1187 
FindStringBegin(char const * const begin,std::string & value,bool remove)1188 bool CommandLine::FindStringBegin(char const* const begin,std::string & value, bool remove) {
1189 	size_t len = strlen(begin);
1190 	for (cmd_it it = cmds.begin(); it != cmds.end();++it) {
1191 		if (strncmp(begin,(*it).c_str(),len)==0) {
1192 			value=((*it).c_str() + len);
1193 			if (remove) cmds.erase(it);
1194 			return true;
1195 		}
1196 	}
1197 	return false;
1198 }
1199 
FindStringRemain(char const * const name,std::string & value)1200 bool CommandLine::FindStringRemain(char const * const name,std::string & value) {
1201 	cmd_it it;value.clear();
1202 	if (!FindEntry(name,it)) return false;
1203 	++it;
1204 	for (;it != cmds.end();++it) {
1205 		value += " ";
1206 		value += (*it);
1207 	}
1208 	return true;
1209 }
1210 
1211 /* Only used for parsing command.com /C
1212  * Allowing /C dir and /Cdir
1213  * Restoring quotes back into the commands so command /C mount d "/tmp/a b" works as intended
1214  */
FindStringRemainBegin(char const * const name,std::string & value)1215 bool CommandLine::FindStringRemainBegin(char const * const name,std::string & value) {
1216 	cmd_it it;value.clear();
1217 	if (!FindEntry(name,it)) {
1218 		size_t len = strlen(name);
1219 			for (it = cmds.begin();it != cmds.end();++it) {
1220 				if (strncasecmp(name,(*it).c_str(),len)==0) {
1221 					std::string temp = ((*it).c_str() + len);
1222 					//Restore quotes for correct parsing in later stages
1223 					if(temp.find(' ') != std::string::npos)
1224 						value = std::string("\"") + temp + std::string("\"");
1225 					else
1226 						value = temp;
1227 					break;
1228 				}
1229 			}
1230 		if (it == cmds.end()) return false;
1231 	}
1232 	++it;
1233 	for (;it != cmds.end();++it) {
1234 		value += " ";
1235 		std::string temp = (*it);
1236 		if(temp.find(' ') != std::string::npos)
1237 			value += std::string("\"") + temp + std::string("\"");
1238 		else
1239 			value += temp;
1240 	}
1241 	return true;
1242 }
1243 
GetStringRemain(std::string & value)1244 bool CommandLine::GetStringRemain(std::string & value) {
1245 	if (!cmds.size()) return false;
1246 
1247 	cmd_it it = cmds.begin();value = (*it++);
1248 	for(;it != cmds.end();++it) {
1249 		value += " ";
1250 		value += (*it);
1251 	}
1252 	return true;
1253 }
1254 
1255 
GetCount(void)1256 unsigned int CommandLine::GetCount(void) {
1257 	return (unsigned int)cmds.size();
1258 }
1259 
FillVector(std::vector<std::string> & vector)1260 void CommandLine::FillVector(std::vector<std::string> & vector) {
1261 	for(cmd_it it = cmds.begin(); it != cmds.end(); ++it) {
1262 		vector.push_back((*it));
1263 	}
1264 #ifdef WIN32
1265 	// add back the \" if the parameter contained a space
1266 	for(Bitu i = 0; i < vector.size(); i++) {
1267 		if(vector[i].find(' ') != std::string::npos) {
1268 			vector[i] = "\""+vector[i]+"\"";
1269 		}
1270 	}
1271 #endif
1272 }
1273 
GetParameterFromList(const char * const params[],std::vector<std::string> & output)1274 int CommandLine::GetParameterFromList(const char* const params[], std::vector<std::string> & output) {
1275 	// return values: 0 = P_NOMATCH, 1 = P_NOPARAMS
1276 	// TODO return nomoreparams
1277 	int retval = 1;
1278 	output.clear();
1279 	enum {
1280 		P_START, P_FIRSTNOMATCH, P_FIRSTMATCH
1281 	} parsestate = P_START;
1282 	cmd_it it = cmds.begin();
1283 	while(it != cmds.end()) {
1284 		bool found = false;
1285 		for (int i = 0; *params[i] != 0; ++i) {
1286 			if (!strcasecmp((*it).c_str(),params[i])) {
1287 				// found a parameter
1288 				found = true;
1289 				switch(parsestate) {
1290 				case P_START:
1291 					retval = i+2;
1292 					parsestate = P_FIRSTMATCH;
1293 					break;
1294 				case P_FIRSTMATCH:
1295 				case P_FIRSTNOMATCH:
1296 					return retval;
1297 				}
1298 			}
1299 		}
1300 		if(!found)
1301 			switch(parsestate) {
1302 			case P_START:
1303 				retval = 0; // no match
1304 				parsestate = P_FIRSTNOMATCH;
1305 				output.push_back(*it);
1306 				break;
1307 			case P_FIRSTMATCH:
1308 			case P_FIRSTNOMATCH:
1309 				output.push_back(*it);
1310 				break;
1311 			}
1312 		cmd_it itold = it;
1313 		++it;
1314 		cmds.erase(itold);
1315 
1316 	}
1317 
1318 	return retval;
1319 /*
1320 bool CommandLine::FindEntry(char const * const name,cmd_it & it,bool neednext) {
1321 	for (it=cmds.begin();it!=cmds.end();it++) {
1322 		if (!strcasecmp((*it).c_str(),name)) {
1323 			cmd_it itnext=it;itnext++;
1324 			if (neednext && (itnext==cmds.end())) return false;
1325 			return true;
1326 		}
1327 	}
1328 	return false;
1329 */
1330 
1331 
1332 /*
1333 	cmd_it it=cmds.begin();value=(*it++);
1334 	while(it != cmds.end()) {
1335 		if(params.
1336 
1337 		it++;
1338 	}
1339 */
1340 	// find next parameter
1341 	//return -1;
1342 
1343 }
1344 
CommandLine(int argc,char const * const argv[])1345 CommandLine::CommandLine(int argc, char const *const argv[])
1346 {
1347 	if (argc>0) {
1348 		file_name=argv[0];
1349 	}
1350 	int i=1;
1351 	while (i<argc) {
1352 		cmds.push_back(argv[i]);
1353 		i++;
1354 	}
1355 }
1356 
Get_arglength()1357 Bit16u CommandLine::Get_arglength()
1358 {
1359 	if (cmds.empty())
1360 		return 0;
1361 
1362 	size_t total_length = 0;
1363 	for (const auto &cmd : cmds)
1364 		total_length += cmd.size() + 1;
1365 
1366 	if (total_length > UINT16_MAX) {
1367 		LOG_MSG("SETUP: Command line length too long, truncating");
1368 		total_length = UINT16_MAX;
1369 	}
1370 	return static_cast<uint16_t>(total_length);
1371 }
1372 
CommandLine(const char * name,const char * cmdline)1373 CommandLine::CommandLine(const char *name, const char *cmdline)
1374 {
1375 	if (name) file_name=name;
1376 	/* Parse the cmds and put them in the list */
1377 	bool inword,inquote;char c;
1378 	inword=false;inquote=false;
1379 	std::string str;
1380 	const char * c_cmdline=cmdline;
1381 	while ((c=*c_cmdline)!=0) {
1382 		if (inquote) {
1383 			if (c!='"') str+=c;
1384 			else {
1385 				inquote=false;
1386 				cmds.push_back(str);
1387 				str.erase();
1388 			}
1389 		} else if (inword) {
1390 			if (c!=' ') str+=c;
1391 			else {
1392 				inword=false;
1393 				cmds.push_back(str);
1394 				str.erase();
1395 			}
1396 		}
1397 		else if (c=='\"') { inquote=true;}
1398 		else if (c!=' ') { str+=c;inword=true;}
1399 		c_cmdline++;
1400 	}
1401 	if (inword || inquote) cmds.push_back(str);
1402 }
1403 
Shift(unsigned int amount)1404 void CommandLine::Shift(unsigned int amount) {
1405 	while(amount--) {
1406 		file_name = cmds.size()?(*(cmds.begin())):"";
1407 		if(cmds.size()) cmds.erase(cmds.begin());
1408 	}
1409 }
1410 
1411 // Parse the user's configuration files starting with the primary, then custom
1412 // -conf's, and finally the local dosbox.conf
SETUP_ParseConfigFiles(const std::string & config_path)1413 void SETUP_ParseConfigFiles(const std::string &config_path)
1414 {
1415 	std::string config_file;
1416 
1417 	// First: parse the user's primary config file
1418 	const bool wants_primary_conf = !control->cmdline->FindExist("-noprimaryconf", true);
1419 	if (wants_primary_conf) {
1420 		Cross::GetPlatformConfigName(config_file);
1421 		const std::string config_combined = config_path + config_file;
1422 		control->ParseConfigFile("primary", config_combined);
1423 	}
1424 
1425 	// Second: parse the local 'dosbox.conf', if present
1426 	const bool wants_local_conf = !control->cmdline->FindExist("-nolocalconf", true);
1427 	if (wants_local_conf) {
1428 		control->ParseConfigFile("local", "dosbox.conf");
1429 	}
1430 
1431 	// Finally: layer on custom -conf <files>
1432 	while (control->cmdline->FindString("-conf", config_file, true)) {
1433 		if (!control->ParseConfigFile("custom", config_file)) {
1434 			// try to load it from the user directory
1435 			if (!control->ParseConfigFile("custom", config_path + config_file)) {
1436 				LOG_MSG("CONFIG: Can't open custom conf file: %s",
1437 				        config_file.c_str());
1438 			}
1439 		}
1440 	}
1441 
1442 	// Create a new primary if permitted and no other conf was loaded
1443 	if (wants_primary_conf && !control->configfiles.size()) {
1444 		std::string new_config_path = config_path;
1445 		Cross::CreatePlatformConfigDir(new_config_path);
1446 		Cross::GetPlatformConfigName(config_file);
1447 		const std::string config_combined = new_config_path + config_file;
1448 		if (control->PrintConfig(config_combined)) {
1449 			LOG_MSG("CONFIG: Wrote new primary conf file '%s'", config_combined.c_str());
1450 			control->ParseConfigFile("new primary", config_combined);
1451 		} else {
1452 			LOG_WARNING("CONFIG: Unable to write a new primary conf file '%s'",
1453 						config_combined.c_str());
1454 		}
1455 	}
1456 }
1457