1
0
Fork 0

Add new features to the config command for control of the config file:

- manipulate the autoexec section
- display information on sections and values
- show the used config files and startup command line parameters 
- restart capability
- save config files either in the config or program directory

Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@3651
This commit is contained in:
Ralf Grillenberger 2010-10-28 17:35:45 +00:00
parent 0d7b792d05
commit 143beec6b1
15 changed files with 847 additions and 248 deletions

View file

@ -19,6 +19,7 @@
/* $Id: programs.cpp,v 1.37 2009-05-27 09:15:42 qbix79 Exp $ */
#include <vector>
#include <sstream>
#include <ctype.h>
#include <stdlib.h>
#include <stdarg.h>
@ -78,7 +79,7 @@ static Bitu PROGRAMS_Handler(void) {
HostPt writer=(HostPt)&index;
for (;size>0;size--) *writer++=mem_readb(reader++);
Program * new_program;
if(index > internal_progs.size()) E_Exit("something is messing with the memory");
if (index > internal_progs.size()) E_Exit("something is messing with the memory");
PROGRAMS_Main * handler = internal_progs[index];
(*handler)(&new_program);
new_program->Run();
@ -120,7 +121,7 @@ void Program::ChangeToLongCmd() {
* Length of arguments can be ~120. but switch when above 100 to be sure
*/
if(/*control->SecureMode() ||*/ cmd->Get_arglength() > 100) {
if (/*control->SecureMode() ||*/ cmd->Get_arglength() > 100) {
CommandLine* temp = new CommandLine(cmd->GetFileName(),full_arguments.c_str());
delete cmd;
cmd = temp;
@ -139,7 +140,7 @@ void Program::WriteOut(const char * format,...) {
Bit16u size = (Bit16u)strlen(buf);
for(Bit16u i = 0; i < size;i++) {
Bit8u out;Bit16u s=1;
if(buf[i] == 0xA && i > 0 && buf[i-1] !=0xD) {
if (buf[i] == 0xA && i > 0 && buf[i-1] !=0xD) {
out = 0xD;DOS_WriteFile(STDOUT,&out,&s);
}
out = buf[i];
@ -154,7 +155,7 @@ void Program::WriteOut_NoParsing(const char * format) {
char const* buf = format;
for(Bit16u i = 0; i < size;i++) {
Bit8u out;Bit16u s=1;
if(buf[i] == 0xA && i > 0 && buf[i-1] !=0xD) {
if (buf[i] == 0xA && i > 0 && buf[i-1] !=0xD) {
out = 0xD;DOS_WriteFile(STDOUT,&out,&s);
}
out = buf[i];
@ -243,149 +244,457 @@ bool Program::SetEnv(const char * entry,const char * new_string) {
return true;
}
bool MSG_Write(const char *);
void restart_program(std::vector<std::string> & parameters);
class CONFIG : public Program {
public:
void Run(void);
};
void MSG_Write(const char *);
void CONFIG::Run(void) {
FILE * f;
if (cmd->FindString("-writeconf",temp_line,true)
|| cmd->FindString("-wc",temp_line,true)) {
/* In secure mode don't allow a new configfile to be created */
if(control->SecureMode()) {
WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW"));
return;
private:
void restart(const char* useconfig);
void writeconf(std::string name, bool configdir) {
if (configdir) {
// write file to the default config directory
std::string config_path;
Cross::GetPlatformConfigDir(config_path);
name = config_path + name;
}
f=fopen(temp_line.c_str(),"wb+");
if (!f) {
WriteOut(MSG_Get("PROGRAM_CONFIG_FILE_ERROR"),temp_line.c_str());
return;
WriteOut(MSG_Get("PROGRAM_CONFIG_FILE_WHICH"),name.c_str());
if (!control->PrintConfig(name.c_str())) {
WriteOut(MSG_Get("PROGRAM_CONFIG_FILE_ERROR"),name.c_str());
}
fclose(f);
control->PrintConfig(temp_line.c_str());
return;
}
if (cmd->FindString("-writelang",temp_line,true)
||cmd->FindString("-wl",temp_line,true)) {
/* In secure mode don't allow a new languagefile to be created
* Who knows which kind of file we would overwriting. */
if(control->SecureMode()) {
WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW"));
return;
}
f=fopen(temp_line.c_str(),"wb+");
if (!f) {
WriteOut(MSG_Get("PROGRAM_CONFIG_FILE_ERROR"),temp_line.c_str());
return;
}
fclose(f);
MSG_Write(temp_line.c_str());
return;
}
/* Code for switching to secure mode */
if(cmd->FindExist("-securemode",true)) {
control->SwitchToSecureMode();
WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_ON"));
return;
}
/* Code for getting the current configuration. *
* Official format: config -get "section property" *
* As a bonus it will set %CONFIG% to this value as well */
if(cmd->FindString("-get",temp_line,true)) {
std::string temp2 = "";
cmd->GetStringRemain(temp2);//So -get n1 n2= can be used without quotes
if(temp2 != "") temp_line = temp_line + " " + temp2;
std::string::size_type space = temp_line.find(" ");
if(space == std::string::npos) {
WriteOut(MSG_Get("PROGRAM_CONFIG_GET_SYNTAX"));
return;
}
//Copy the found property to a new string and erase from templine (mind the space)
std::string prop = temp_line.substr(space+1); temp_line.erase(space);
Section* sec = control->GetSection(temp_line.c_str());
if(!sec) {
WriteOut(MSG_Get("PROGRAM_CONFIG_SECTION_ERROR"),temp_line.c_str());
return;
}
std::string val = sec->GetPropValue(prop.c_str());
if(val == NO_SUCH_PROPERTY) {
WriteOut(MSG_Get("PROGRAM_CONFIG_NO_PROPERTY"),prop.c_str(),temp_line.c_str());
return;
}
WriteOut("%s",val.c_str());
first_shell->SetEnv("CONFIG",val.c_str());
return;
}
/* Code for the configuration changes *
* Official format: config -set "section property=value" *
* Accepted: without quotes and/or without -set and/or without section *
* and/or the "=" replaced by a " " */
if (cmd->FindString("-set",temp_line,true)) { //get all arguments
std::string temp2 = "";
cmd->GetStringRemain(temp2);//So -set n1 n2=n3 can be used without quotes
if(temp2!="") temp_line = temp_line + " " + temp2;
} else if(!cmd->GetStringRemain(temp_line)) {//no set
WriteOut(MSG_Get("PROGRAM_CONFIG_USAGE")); //and no arguments specified
return;
};
//Wanted input: n1 n2=n3
char copy[1024];
strcpy(copy,temp_line.c_str());
//seperate section from property
const char* temp = strchr(copy,' ');
if((temp && *temp) || (temp=strchr(copy,'=')) ) copy[temp++ - copy]= 0;
else {
WriteOut(MSG_Get("PROGRAM_CONFIG_USAGE"));
return;
}
//if n1 n2 n3 then replace last space with =
const char* sign = strchr(temp,'=');
if(!sign) {
sign = strchr(temp,' ');
if(sign) {
copy[sign - copy] = '=';
} else {
//2 items specified (no space nor = between n2 and n3
//assume that they posted: property value
//Try to determine the section.
Section* sec=control->GetSectionFromProperty(copy);
if(!sec){
if(control->GetSectionFromProperty(temp)) return; //Weird situation:ignore
WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR"),copy);
return;
} //Hack to allow config ems true
char buffer[1024];strcpy(buffer,copy);strcat(buffer,"=");strcat(buffer,temp);
sign = strchr(buffer,' ');
if(sign) buffer[sign - buffer] = '=';
strcpy(copy,sec->GetName());
temp = buffer;
}
}
/* Input processed. Now the real job starts
* copy contains the likely "sectionname"
* temp contains "property=value"
* the section is destroyed and a new input line is given to
* the configuration parser. Then the section is restarted.
*/
char* inputline = const_cast<char*>(temp);
Section* sec = 0;
sec = control->GetSection(copy);
if(!sec) { WriteOut(MSG_Get("PROGRAM_CONFIG_SECTION_ERROR"),copy);return;}
sec->ExecuteDestroy(false);
sec->HandleInputline(inputline);
sec->ExecuteInit(false);
bool securemode_check() {
if (control->SecureMode()) {
WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW"));
return true;
}
return false;
}
};
void CONFIG::Run(void) {
static const char* const params[] = {
"-r", "-wcp", "-wcd", "-wc", "-writeconf", "-l", "-rmconf",
"-h", "-help", "-?", "-axclear", "-axadd", "-axtype", "-get", "-set",
"-writelang", "-wl", "-securemode", "" };
enum prs {
P_NOMATCH, P_NOPARAMS, // fixed return values for GetParameterFromList
P_RESTART,
P_WRITECONF_PORTABLE, P_WRITECONF_DEFAULT, P_WRITECONF, P_WRITECONF2,
P_LISTCONF, P_KILLCONF,
P_HELP, P_HELP2, P_HELP3,
P_AUTOEXEC_CLEAR, P_AUTOEXEC_ADD, P_AUTOEXEC_TYPE,
P_GETPROP, P_SETPROP,
P_WRITELANG, P_WRITELANG2,
P_SECURE
} presult = P_NOMATCH;
bool first = true;
std::vector<std::string> pvars;
while(presult != P_NOPARAMS) {
presult = (enum prs)cmd->GetParameterFromList(params, pvars);
switch(presult) {
case P_RESTART:
if (securemode_check()) return;
if (pvars.size() == 0) restart_program(control->startup_params);
else {
std::vector<std::string> restart_params;
restart_params.push_back(control->cmdline->GetFileName());
for(size_t i = 0; i < pvars.size(); i++) {
restart_params.push_back(pvars[i]);
if (pvars[i].find(' ') != std::string::npos) {
pvars[i] = "\""+pvars[i]+"\""; // add back spaces
}
}
// the rest on the commandline, too
cmd->FillVector(restart_params);
restart_program(restart_params);
}
return;
case P_LISTCONF: {
Bitu size = control->configfiles.size();
std::string config_path;
Cross::GetPlatformConfigDir(config_path);
WriteOut(MSG_Get("PROGRAM_CONFIG_CONFDIR"), VERSION,config_path.c_str());
if (size==0) WriteOut(MSG_Get("PROGRAM_CONFIG_NOCONFIGFILE"));
else {
WriteOut(MSG_Get("PROGRAM_CONFIG_PRIMARY_CONF"),control->configfiles.front().c_str());
if (size > 1) {
WriteOut(MSG_Get("PROGRAM_CONFIG_ADDITIONAL_CONF"));
for(Bitu i = 1; i < size; i++)
WriteOut("%s\n",control->configfiles[i].c_str());
}
}
if (control->startup_params.size() > 0) {
std::string test;
for(size_t k = 0; k < control->startup_params.size(); k++)
test += control->startup_params[k] + " ";
WriteOut(MSG_Get("PROGRAM_CONFIG_PRINT_STARTUP"), test.c_str());
}
break;
}
case P_WRITECONF: case P_WRITECONF2:
if (securemode_check()) return;
if (pvars.size() > 1) return;
else if (pvars.size() == 1) {
// write config to specific file, except if it is an absolute path
writeconf(pvars[0], !Cross::IsPathAbsolute(pvars[0]));
} else {
// -wc without parameter: write primary config file
if (control->configfiles.size()) writeconf(control->configfiles[0], false);
else WriteOut(MSG_Get("PROGRAM_CONFIG_NOCONFIGFILE"));
}
break;
case P_WRITECONF_DEFAULT: {
// write to /userdir/dosbox0.xx.conf
if (securemode_check()) return;
if (pvars.size() > 0) return;
std::string confname;
Cross::GetPlatformConfigName(confname);
writeconf(confname, true);
break;
}
case P_WRITECONF_PORTABLE:
if (securemode_check()) return;
if (pvars.size() > 1) return;
else if (pvars.size() == 1) {
// write config to startup directory
writeconf(pvars[0], false);
} else {
// -wcp without parameter: write dosbox.conf to startup directory
if (control->configfiles.size()) writeconf(std::string("dosbox.conf"), false);
else WriteOut(MSG_Get("PROGRAM_CONFIG_NOCONFIGFILE"));
}
break;
case P_NOPARAMS:
if (!first) break;
case P_HELP: case P_HELP2: case P_HELP3: {
switch(pvars.size()) {
case 0:
WriteOut(MSG_Get("PROGRAM_CONFIG_USAGE"));
return;
case 1: {
if (!strcasecmp("sections",pvars[0].c_str())) {
// list the sections
WriteOut(MSG_Get("PROGRAM_CONFIG_HLP_SECTLIST"));
Bitu i = 0;
while(true) {
Section* sec = control->GetSection(i++);
if (!sec) break;
WriteOut("%s\n",sec->GetName());
}
return;
}
// if it's a section, leave it as one-param
Section* sec = control->GetSection(pvars[0].c_str());
if (!sec) {
// could be a property
sec = control->GetSectionFromProperty(pvars[0].c_str());
if (!sec) {
WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR"));
return;
}
pvars.insert(pvars.begin(),std::string(sec->GetName()));
}
break;
}
case 2: {
// sanity check
Section* sec = control->GetSection(pvars[0].c_str());
Section* sec2 = control->GetSectionFromProperty(pvars[1].c_str());
if (sec != sec2) {
WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR"));
}
break;
}
default:
WriteOut(MSG_Get("PROGRAM_CONFIG_USAGE"));
return;
}
// if we have one value in pvars, it's a section
// two values are section + property
Section* sec = control->GetSection(pvars[0].c_str());
if (sec==NULL) {
WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR"));
return;
}
Section_prop* psec = dynamic_cast <Section_prop*>(sec);
if (psec==NULL) {
// failed; maybe it's the autoexec section?
Section_line* pline = dynamic_cast <Section_line*>(sec);
if (pline==NULL) E_Exit("Section dynamic cast failed.");
WriteOut(MSG_Get("PROGRAM_CONFIG_HLP_LINEHLP"),
pline->GetName(),
// this is 'unclean' but the autoexec section has no help associated
MSG_Get("AUTOEXEC_CONFIGFILE_HELP"),
pline->data.c_str() );
return;
}
if (pvars.size()==1) {
size_t i = 0;
WriteOut(MSG_Get("PROGRAM_CONFIG_HLP_SECTHLP"),pvars[0].c_str());
while(true) {
// list the properties
Property* p = psec->Get_prop(i++);
if (p==NULL) break;
WriteOut("%s\n", p->propname.c_str());
}
} else {
// find the property by it's name
size_t i = 0;
while (true) {
Property *p = psec->Get_prop(i++);
if (p==NULL) break;
if (!strcasecmp(p->propname.c_str(),pvars[1].c_str())) {
// found it; make the list of possible values
std::string propvalues;
std::vector<Value> pv = p->GetValues();
if (p->Get_type()==Value::Etype::V_BOOL) {
// possible values for boolean are true, false
propvalues += "true, false";
} else if (p->Get_type()==Value::Etype::V_INT) {
// print min, max for integer values if used
Prop_int* pint = dynamic_cast <Prop_int*>(p);
if (pint==NULL) E_Exit("Int property dynamic cast failed.");
if (pint->getMin() != pint->getMax()) {
std::ostringstream oss;
oss << pint->getMin();
oss << "..";
oss << pint->getMax();
propvalues += oss.str();
}
}
for(Bitu k = 0; k < pv.size(); k++) {
if (pv[k].ToString() =="%u")
propvalues += MSG_Get("PROGRAM_CONFIG_HLP_POSINT");
else propvalues += pv[k].ToString();
if ((k+1) < pv.size()) propvalues += ", ";
}
WriteOut(MSG_Get("PROGRAM_CONFIG_HLP_PROPHLP"),
p->propname.c_str(),
sec->GetName(),
p->Get_help(),propvalues.c_str(),
p->Get_Default_Value().ToString().c_str(),
p->GetValue().ToString().c_str());
// print 'changability'
if (p->getChange()==Property::Changeable::OnlyAtStart) {
WriteOut(MSG_Get("PROGRAM_CONFIG_HLP_NOCHANGE"));
}
return;
}
}
break;
}
return;
}
case P_AUTOEXEC_CLEAR: {
Section_line* sec = dynamic_cast <Section_line*>
(control->GetSection(std::string("autoexec")));
sec->data.clear();
break;
}
case P_AUTOEXEC_ADD: {
if (pvars.size() == 0) {
WriteOut(MSG_Get("PROGRAM_CONFIG_MISSINGPARAM"));
return;
}
Section_line* sec = dynamic_cast <Section_line*>
(control->GetSection(std::string("autoexec")));
for(Bitu i = 0; i < pvars.size(); i++) sec->HandleInputline(pvars[i]);
break;
}
case P_AUTOEXEC_TYPE: {
Section_line* sec = dynamic_cast <Section_line*>
(control->GetSection(std::string("autoexec")));
WriteOut("\n%s",sec->data.c_str());
break;
}
case P_GETPROP: {
// "section property"
// "property"
// "section" "property"
if (pvars.size()==0) {
WriteOut(MSG_Get("PROGRAM_CONFIG_GET_SYNTAX"));
return;
}
std::string::size_type spcpos = pvars[0].find_first_of(' ');
// split on the ' '
if (spcpos != std::string::npos) {
pvars.insert(++pvars.begin(),pvars[0].substr(spcpos+1));
pvars[0].erase(spcpos);
}
switch(pvars.size()) {
case 1: {
// property only
Section* sec = control->GetSectionFromProperty(pvars[0].c_str());
if (!sec) {
WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR"));
return;
}
std::string val = sec->GetPropValue(pvars[0].c_str());
WriteOut("%s",val.c_str());
break;
}
case 2: {
// section + property
Section* sec = control->GetSection(pvars[0].c_str());
if (!sec) {
WriteOut(MSG_Get("PROGRAM_CONFIG_SECTION_ERROR"));
return;
}
std::string val = sec->GetPropValue(pvars[1].c_str());
if (val == NO_SUCH_PROPERTY) {
WriteOut(MSG_Get("PROGRAM_CONFIG_NO_PROPERTY"),pvars[1].c_str(),pvars[0].c_str());
return;
}
WriteOut("%s",val.c_str());
break;
}
}
return;
}
case P_SETPROP: case P_NOMATCH: {
// Code for the configuration changes
// Official format: config -set "section property=value"
// Accepted: with or without -set,
// "section property value"
// "section property=value"
// "property" "value"
// "section" "property=value"
// "section" "property=value" "value" "value" ...
// "section" "property" "value" "value" ...
// "section property" "value" "value" ...
// "property" "value" "value" ...
// "property=value" "value" "value" ...
if (pvars.size()==0) {
WriteOut(MSG_Get("PROGRAM_CONFIG_SET_SYNTAX"));
return;
}
// add rest of command
std::string rest;
if (cmd->GetStringRemain(rest)) pvars.push_back(rest);
// attempt to split off the first word
std::string::size_type spcpos = pvars[0].find_first_of(' ');
std::string::size_type equpos = pvars[0].find_first_of('=');
if ((equpos != std::string::npos) &&
((spcpos == std::string::npos) || (equpos < spcpos))) {
// If we have a '=' possibly before a ' ' split on the =
pvars.insert(++pvars.begin(),pvars[0].substr(equpos+1));
pvars[0].erase(equpos);
// As we had a = the first thing must be a property now
Section* sec=control->GetSectionFromProperty(pvars[0].c_str());
if (sec) pvars.insert(pvars.begin(),std::string(sec->GetName()));
else {
WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR"));
return;
}
// order in the vector should be ok now
} else {
if ((spcpos != std::string::npos) &&
((equpos == std::string::npos) || (spcpos < equpos))) {
// ' ' before a possible '=', split on the ' '
pvars.insert(++pvars.begin(),pvars[0].substr(spcpos+1));
pvars[0].erase(spcpos);
}
// check if the first parameter is a section or property
Section* sec = control->GetSection(pvars[0].c_str());
if (!sec) {
// not a section: little duplicate from above
Section* sec=control->GetSectionFromProperty(pvars[0].c_str());
if (sec) pvars.insert(pvars.begin(),std::string(sec->GetName()));
else {
WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR"));
return;
}
} else {
// first of pvars is most likely a section, but could still be gus
// have a look at the second parameter
if (pvars.size() < 2) {
WriteOut(MSG_Get("PROGRAM_CONFIG_SET_SYNTAX"));
return;
}
std::string::size_type spcpos2 = pvars[1].find_first_of(' ');
std::string::size_type equpos2 = pvars[1].find_first_of('=');
if ((equpos2 != std::string::npos) &&
((spcpos2 == std::string::npos) || (equpos2 < spcpos2))) {
// split on the =
pvars.insert(pvars.begin()+2,pvars[1].substr(equpos2+1));
pvars[1].erase(equpos2);
} else if ((spcpos2 != std::string::npos) &&
((equpos2 == std::string::npos) || (spcpos2 < equpos2))) {
// split on the ' '
pvars.insert(pvars.begin()+2,pvars[1].substr(spcpos2+1));
pvars[1].erase(spcpos2);
}
// is this a property?
Section* sec2 = control->GetSectionFromProperty(pvars[1].c_str());
if (!sec2) {
// not a property,
Section* sec3 = control->GetSectionFromProperty(pvars[0].c_str());
if (sec3) {
// section and property name are identical
pvars.insert(pvars.begin(),pvars[0]);
} // else has been checked above already
}
}
}
// Input has been parsed (pvar[0]=section, [1]=property, [2]=value
// now execute
Section* tsec = control->GetSection(pvars[0]);
std::string value;
value += pvars[2];
for(Bitu i = 3; i < pvars.size(); i++) value += (std::string(" ") + pvars[i]);
std::string inputline = pvars[1] + "=" + value;
tsec->ExecuteDestroy(false);
bool change_success = tsec->HandleInputline(inputline.c_str());
if (!change_success) WriteOut(MSG_Get("PROGRAM_CONFIG_VALUE_ERROR"),
value.c_str(),pvars[1].c_str());
tsec->ExecuteInit(false);
return;
}
case P_WRITELANG: case P_WRITELANG2:
// In secure mode don't allow a new languagefile to be created
// Who knows which kind of file we would overwrite.
if (securemode_check()) return;
if (pvars.size() < 1) {
WriteOut(MSG_Get("PROGRAM_CONFIG_MISSINGPARAM"));
return;
}
if (!MSG_Write(pvars[0].c_str())) {
WriteOut(MSG_Get("PROGRAM_CONFIG_FILE_ERROR"),pvars[0].c_str());
return;
}
break;
case P_SECURE:
// Code for switching to secure mode
control->SwitchToSecureMode();
WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_ON"));
return;
default:
E_Exit("bug");
break;
}
first = false;
}
return;
}
@ -401,12 +710,48 @@ void PROGRAMS_Init(Section* /*sec*/) {
CALLBACK_Setup(call_program,&PROGRAMS_Handler,CB_RETF,"internal program");
PROGRAMS_MakeFile("CONFIG.COM",CONFIG_ProgramStart);
MSG_Add("PROGRAM_CONFIG_FILE_ERROR","Can't open file %s\n");
MSG_Add("PROGRAM_CONFIG_USAGE","Config tool:\nUse -writeconf filename to write the current config.\nUse -writelang filename to write the current language strings.\n");
// listconf
MSG_Add("PROGRAM_CONFIG_NOCONFIGFILE","No config file loaded!\n");
MSG_Add("PROGRAM_CONFIG_PRIMARY_CONF","Primary config file: \n%s\n");
MSG_Add("PROGRAM_CONFIG_ADDITIONAL_CONF","Additional config files:\n");
MSG_Add("PROGRAM_CONFIG_CONFDIR","DOSBox %s configuration directory: \n%s\n\n");
// writeconf
MSG_Add("PROGRAM_CONFIG_FILE_ERROR","\nCan't open file %s\n");
MSG_Add("PROGRAM_CONFIG_FILE_WHICH","Writing config file %s");
// help
MSG_Add("PROGRAM_CONFIG_USAGE","Config tool:\n"\
"-writeconf or -wc without parameter: write to primary loaded config file.\n"\
"-writeconf or -wc with filename: write file to config directory.\n"\
"Use -writelang or -wl filename to write the current language strings.\n"\
"-r [parameters]\n Restart DOSBox, either using the previous parameters or any that are appended.\n"\
"-wcp [filename]\n Write config file to the program directory, dosbox.conf or the specified \n filename.\n"\
"-wcd\n Write to the default config file in the config directory.\n"\
"-l lists configuration parameters.\n"\
"-h, -help, -? sections / sectionname / propertyname\n"\
" Without parameters, displays this help screen. Add \"sections\" for a list of\n sections."\
" For info about a specific section or property add it's name behind.\n"\
"-axclear clears the autoexec section.\n"\
"-axadd [line] adds a line to the autoexec section.\n"\
"-axtype prints the content of the autoexec section.\n"\
"-securemode switches to secure mode.\n"\
"-get \"section property\" returns the value of the property.\n"\
"-set \"section property=value\" sets the value." );
MSG_Add("PROGRAM_CONFIG_HLP_PROPHLP","Purpose of property \"%s\" (contained in section \"%s\"):\n%s\n\nPossible Values: %s\nDefault value: %s\nCurrent value: %s\n");
MSG_Add("PROGRAM_CONFIG_HLP_LINEHLP","Purpose of section \"%s\":\n%s\nCurrent value:\n%s\n");
MSG_Add("PROGRAM_CONFIG_HLP_NOCHANGE","This property cannot be changed at runtime.\n");
MSG_Add("PROGRAM_CONFIG_HLP_POSINT","positive integer");
MSG_Add("PROGRAM_CONFIG_HLP_SECTHLP","Section %s contains the following properties:\n");
MSG_Add("PROGRAM_CONFIG_HLP_SECTLIST","DOSBox configuration contains the following sections:\n\n");
MSG_Add("PROGRAM_CONFIG_SECURE_ON","Switched to secure mode.\n");
MSG_Add("PROGRAM_CONFIG_SECURE_DISALLOW","This operation is not permitted in secure mode.\n");
MSG_Add("PROGRAM_CONFIG_SECTION_ERROR","Section %s doesn't exist.\n");
MSG_Add("PROGRAM_CONFIG_VALUE_ERROR","\"%s\" is not a valid value for property %s.\n");
MSG_Add("PROGRAM_CONFIG_PROPERTY_ERROR","No such section or property.\n");
MSG_Add("PROGRAM_CONFIG_NO_PROPERTY","There is no property %s in section %s.\n");
MSG_Add("PROGRAM_CONFIG_GET_SYNTAX","Correct syntax: config -get \"section property\".\n");
MSG_Add("PROGRAM_CONFIG_PRINT_STARTUP","\nDOSBox was started with the following command line parameters:\n%s");
MSG_Add("PROGRAM_CONFIG_MISSINGPARAM","Missing parameter.");
}