diff --git a/README b/README index 7302d250..b8793b92 100644 --- a/README +++ b/README @@ -622,7 +622,19 @@ VER set major_version [minor_version] CONFIG -writeconf filelocation +CONFIG -writeconf +CONFIG -wcp filelocation +CONFIG -wcd CONFIG -writelang filelocation +CONFIG -axadd +CONFIG -axclear +CONFIG -axtype +CONFIG -r [parameters] +CONFIG -l +CONFIG -help +CONFIG -help sections +CONFIG -help section +CONFIG -help section property CONFIG -securemode CONFIG -set "section property=value" CONFIG -get "section property" @@ -633,20 +645,74 @@ CONFIG -get "section property" be found in Section 13: "The configuration (options) file". -writeconf filelocation - Write the current configuration settings to a file in a specified location. - "filelocation" is located on the local drive, not a mounted drive in DOSBox. + (or -wc filelocation) + Write the current configuration settings to a file in a specified location + relative to the DOSBox config directory. Relative and absolute paths are + possible. "filelocation" is located on the local drive, not a mounted + drive in DOSBox. + The configuration file controls various settings of DOSBox: the amount of emulated memory, the emulated sound cards and many more things. It allows access to AUTOEXEC.BAT as well. See Section 13: "The configuration (options) file" for more information. + -writeconf + (or -wc) + Write the configuration to the primary loaded config file. + + -wcp filelocation + Write the current configuration settings to the specified file in or + relative to the DOSBox program start directory. Realtive and absolute + paths are possible. This is located on a drive on the host, not a mounted + drive in DOSBox. It is useful if you keep DOSBox on a removable media. + If file is omitted, the configuration will be written to dosbox.conf. + + -wcd + Write the current configuration to the default config file. + + -writelang filelocation + (or -wl filelocation) Write the current language settings to a file in a specified location. "filelocation" is located on the local drive, not a mounted drive in DOSBox. The language file controls all visible output of the internal commands and the internal DOS. See Section 14: "The Language File" for more information. + -axadd "line1" "line2" ... + Adds a command line to the autoexec section. + + -axclear + Clears the autoexec section. + + -axtype + Prints the content of the autoexec section. + + -r [parameters] + Restart DOSBox, either with the parameters that were used to start the + current instance or any that are appended. + + -l + lists DOSBox parameters: + - the configuration directory + - the config files that were used when starting this session + - the command line parameters DOSBox was started with + + -h, -help, -? + Displays an overvie of the config commands. + + -h, -help, -? sections + Displays the list of sections in the config file. + + -h, -help, -? section + Displays the list of properties contained in the specified section. + + -h, -help, -? section property + Shows information about the specified property in the specified section: + - purpose of the property + - possible values, current value, default value + - wether it can definitely not be changed at runtime + -securemode Switches DOSBox to a more secure mode. In this mode the internal commands MOUNT, IMGMOUNT and BOOT won't work. It's not possible either @@ -655,7 +721,6 @@ CONFIG -get "section property" -set "section property=value" CONFIG will attempt to set the property to new value. - Currently CONFIG can not report whether the command succeeded or not. -get "section property" The current value of the property is reported and stored in the @@ -675,7 +740,22 @@ CONFIG -get "section property" config -set "dos ems=false" 4. To check which cpu core is being used. config -get "cpu core" - + 5. To view the list of possible cpu cores: + config -help cpu core + 6. To change the machine type and restart: + config -set "machine cga" + config -wc -r + 7. To configure the autoexec section to auto-mount a directory at start: + config -axadd "mount c c:\dosgames" "c:" + config -wc + 8. To create a specific config file in the config directory: + config -set "dos ems=false" + config -set "cpu cycles=10000" + config -set "core dynamic" + config -axadd "mount c c:\dosgames" "c:" "cd my_game" "my_game" + config -wc my_config.conf + 9. To restart DOSBox from a specific config file in the config directory: + config -r -conf my_config.conf LOADFIX [-size] [program] [program-parameters] LOADFIX -f @@ -985,6 +1065,7 @@ CTRL-F10 Capture/Release the mouse. CTRL-F11 Slow down emulation (Decrease DOSBox Cycles). CTRL-F12 Speed up emulation (Increase DOSBox Cycles)*. ALT-F12 Unlock speed (turbo button/fast forward)**. +CTRL-ALT-HOME Restart DOSBox. F11, ALT-F11 (machine=cga) change tint in NTSC output modes*** F11 (machine=hercules) cycle through amber, green, white colouring*** diff --git a/include/control.h b/include/control.h index 0a688e14..2c157277 100644 --- a/include/control.h +++ b/include/control.h @@ -63,7 +63,14 @@ private: void (* _start_function)(void); bool secure_mode; //Sandbox mode public: - Config(CommandLine * cmd):cmdline(cmd),secure_mode(false){} + bool initialised; + std::vector startup_params; + std::vector configfiles; + Config(CommandLine * cmd):cmdline(cmd),secure_mode(false) { + startup_params.push_back(cmdline->GetFileName()); + cmdline->FillVector(startup_params); + initialised=false; + } ~Config(); Section_line * AddSection_line(char const * const _name,void (*_initfunction)(Section*)); diff --git a/include/cross.h b/include/cross.h index 8628beb3..8e2268d1 100644 --- a/include/cross.h +++ b/include/cross.h @@ -74,6 +74,7 @@ public: static void CreatePlatformConfigDir(std::string& in); static void ResolveHomedir(std::string & temp_line); static void CreateDir(std::string const& temp); + static bool Cross::IsPathAbsolute(std::string const& in); }; diff --git a/include/mapper.h b/include/mapper.h index 932f6563..a59322a8 100644 --- a/include/mapper.h +++ b/include/mapper.h @@ -21,7 +21,7 @@ enum MapKeys { MK_f1,MK_f2,MK_f3,MK_f4,MK_f5,MK_f6,MK_f7,MK_f8,MK_f9,MK_f10,MK_f11,MK_f12, - MK_return,MK_kpminus,MK_scrolllock,MK_printscreen,MK_pause + MK_return,MK_kpminus,MK_scrolllock,MK_printscreen,MK_pause,MK_home }; diff --git a/include/programs.h b/include/programs.h index f8473850..96f84b0e 100644 --- a/include/programs.h +++ b/include/programs.h @@ -52,6 +52,8 @@ public: bool FindStringBegin(char const * const begin,std::string & value, bool remove=false); bool FindStringRemain(char const * const name,std::string & value); bool GetStringRemain(std::string & value); + int GetParameterFromList(const char* const params[], std::vector & output); + void FillVector(std::vector & vector); unsigned int GetCount(void); void Shift(unsigned int amount=1); Bit16u Get_arglength(); diff --git a/include/setup.h b/include/setup.h index 5239cd5d..e79af711 100644 --- a/include/setup.h +++ b/include/setup.h @@ -104,18 +104,18 @@ public: operator int () const throw(WrongType); operator double () const throw(WrongType); operator char const* () const throw(WrongType); - void SetValue(std::string const& in,Etype _type = V_CURRENT) throw(WrongType); + bool SetValue(std::string const& in,Etype _type = V_CURRENT) throw(WrongType); std::string ToString() const; private: void destroy() throw(); Value& copy(Value const& in) throw(WrongType); void plaincopy(Value const& in) throw(); - void set_hex(std::string const& in); - void set_int(std::string const&in); - void set_bool(std::string const& in); + bool set_hex(std::string const& in); + bool set_int(std::string const&in); + bool set_bool(std::string const& in); void set_string(std::string const& in); - void set_double(std::string const& in); + bool set_double(std::string const& in); }; class Property { @@ -127,7 +127,7 @@ public: void Set_values(const char * const * in); void Set_help(std::string const& str); char const* Get_help(); - virtual void SetValue(std::string const& str)=0; + virtual bool SetValue(std::string const& str)=0; Value const& GetValue() const { return value;} Value const& Get_Default_Value() const { return default_value; } //CheckValue returns true if value is in suggested_values; @@ -135,10 +135,12 @@ public: //specific features. virtual bool CheckValue(Value const& in, bool warn); //Set interval value to in or default if in is invalid. force always sets the value. - void SetVal(Value const& in, bool forced,bool warn=true) {if(forced || CheckValue(in,warn)) value = in; else value = default_value;} + bool SetVal(Value const& in, bool forced,bool warn=true) { + if(forced || CheckValue(in,warn)) {value = in; return true;} else { value = default_value; return false;}} virtual ~Property(){ } virtual const std::vector& GetValues() const; Value::Etype Get_type(){return default_value.type;} + Changeable::Value getChange() {return change;} protected: Value value; @@ -161,8 +163,10 @@ public: min = _min; max = _max; } + int getMin() { return min;} + int getMax() { return max;} void SetMinMax(Value const& min,Value const& max) {this->min = min; this->max=max;} - void SetValue(std::string const& in); + bool SetValue(std::string const& in); ~Prop_int(){ } virtual bool CheckValue(Value const& in, bool warn); private: @@ -175,7 +179,7 @@ public: :Property(_propname,when){ default_value = value = _value; } - void SetValue(std::string const& input); + bool SetValue(std::string const& input); ~Prop_double(){ } }; @@ -185,7 +189,7 @@ public: :Property(_propname,when) { default_value = value = _value; } - void SetValue(std::string const& in); + bool SetValue(std::string const& in); ~Prop_bool(){ } }; @@ -195,7 +199,7 @@ public: :Property(_propname,when) { default_value = value = _value; } - void SetValue(std::string const& in); + bool SetValue(std::string const& in); virtual bool CheckValue(Value const& in, bool warn); ~Prop_string(){ } }; @@ -207,7 +211,7 @@ public: default_value = value = _value; realpath = _value; } - void SetValue(std::string const& in); + bool SetValue(std::string const& in); ~Prop_path(){ } }; @@ -217,7 +221,7 @@ public: :Property(_propname,when) { default_value = value = _value; } - void SetValue(std::string const& in); + bool SetValue(std::string const& in); ~Prop_hex(){ } }; @@ -248,7 +252,7 @@ public: const char* GetName() const {return sectionname.c_str();} virtual std::string GetPropValue(std::string const& _property) const =0; - virtual void HandleInputline(std::string const& _line)=0; + virtual bool HandleInputline(std::string const& _line)=0; virtual void PrintData(FILE* outfile) const =0; virtual ~Section() { /*Children must call executedestroy ! */} }; @@ -281,7 +285,7 @@ public: Prop_path* Get_path(std::string const& _propname) const; Prop_multival* Get_multival(std::string const& _propname) const; Prop_multival_remain* Get_multivalremain(std::string const& _propname) const; - void HandleInputline(std::string const& gegevens); + bool HandleInputline(std::string const& gegevens); void PrintData(FILE* outfile) const; virtual std::string GetPropValue(std::string const& _property) const; //ExecuteDestroy should be here else the destroy functions use destroyed properties @@ -299,7 +303,7 @@ public: } Section_prop *GetSection() { return section; } const Section_prop *GetSection() const { return section; } - virtual void SetValue(std::string const& input); + virtual bool SetValue(std::string const& input); virtual const std::vector& GetValues() const; ~Prop_multival() { delete section; } }; //value bevat totale string. setvalue zet elk van de sub properties en checked die. @@ -308,7 +312,7 @@ class Prop_multival_remain:public Prop_multival{ public: Prop_multival_remain(std::string const& _propname, Changeable::Value when,std::string const& sep):Prop_multival(_propname,when,sep){ } - virtual void SetValue(std::string const& input); + virtual bool SetValue(std::string const& input); }; @@ -316,7 +320,7 @@ class Section_line: public Section{ public: Section_line(std::string const& _sectionname):Section(_sectionname){} ~Section_line(){ExecuteDestroy(true);} - void HandleInputline(std::string const& gegevens); + bool HandleInputline(std::string const& gegevens); void PrintData(FILE* outfile) const; virtual std::string GetPropValue(std::string const& _property) const; std::string data; diff --git a/src/debug/debug.cpp b/src/debug/debug.cpp index 5b165de5..ae573722 100644 --- a/src/debug/debug.cpp +++ b/src/debug/debug.cpp @@ -2113,7 +2113,7 @@ void DEBUG_SetupConsole(void) { DBGUI_StartUp(); } -static void DEBUG_ShutDown(Section * /*sec*/) { +void DEBUG_ShutDown(Section * /*sec*/) { CBreakpoint::DeleteAll(); CDebugVar::DeleteAll(); curs_set(old_cursor_state); diff --git a/src/dosbox.cpp b/src/dosbox.cpp index e12c9b79..e1b5ed8d 100644 --- a/src/dosbox.cpp +++ b/src/dosbox.cpp @@ -340,7 +340,7 @@ void DOSBOX_Init(void) { Pstring = secprop->Add_string("machine",Property::Changeable::OnlyAtStart,"svga_s3"); Pstring->Set_values(machines); - Pstring->Set_help("The type of machine tries to emulate."); + Pstring->Set_help("The type of machine DOSBox tries to emulate."); Pstring = secprop->Add_path("captures",Property::Changeable::Always,"capture"); Pstring->Set_help("Directory where things like wave, midi, screenshot get captured."); @@ -376,8 +376,8 @@ void DOSBOX_Init(void) { Pmulti = secprop->Add_multi("scaler",Property::Changeable::Always," "); Pmulti->SetValue("normal2x"); - Pmulti->Set_help("Scaler used to enlarge/enhance low resolution modes.\n" - " If 'forced' is appended, then the scaler will be used even if the result might not be desired."); + Pmulti->Set_help("Scaler used to enlarge/enhance low resolution modes. If 'forced' is appended,\n" + "then the scaler will be used even if the result might not be desired."); Pstring = Pmulti->GetSection()->Add_string("type",Property::Changeable::Always,"normal2x"); const char *scalers[] = { @@ -403,7 +403,8 @@ void DOSBOX_Init(void) { "normal", "simple",0 }; Pstring = secprop->Add_string("core",Property::Changeable::WhenIdle,"auto"); Pstring->Set_values(cores); - Pstring->Set_help("CPU Core used in emulation. auto will switch to dynamic if available and appropriate."); + Pstring->Set_help("CPU Core used in emulation. auto will switch to dynamic if available and\n" + "appropriate."); const char* cputype_values[] = { "auto", "386", "386_slow", "486_slow", "pentium_slow", "386_prefetch", 0}; Pstring = secprop->Add_string("cputype",Property::Changeable::Always,"auto"); @@ -418,9 +419,10 @@ void DOSBOX_Init(void) { "Cycles can be set in 3 ways:\n" " 'auto' tries to guess what a game needs.\n" " It usually works, but can fail for certain games.\n" - " 'fixed #number' will set a fixed amount of cycles. This is what you usually need if 'auto' fails.\n" - " (Example: fixed 4000).\n" - " 'max' will allocate as much cycles as your computer is able to handle.\n"); + " 'fixed #number' will set a fixed amount of cycles. This is what you usually\n" + " need if 'auto' fails (Example: fixed 4000).\n" + " 'max' will allocate as much cycles as your computer is able to\n" + " handle."); const char* cyclest[] = { "auto","fixed","max","%u",0 }; Pstring = Pmulti_remain->GetSection()->Add_string("type",Property::Changeable::Always,"auto"); @@ -431,7 +433,7 @@ void DOSBOX_Init(void) { Pint = secprop->Add_int("cycleup",Property::Changeable::Always,10); Pint->SetMinMax(1,1000000); - Pint->Set_help("Amount of cycles to decrease/increase with keycombo.(CTRL-F11/CTRL-F12)"); + Pint->Set_help("Amount of cycles to decrease/increase with keycombos.(CTRL-F11/CTRL-F12)"); Pint = secprop->Add_int("cycledown",Property::Changeable::Always,20); Pint->SetMinMax(1,1000000); diff --git a/src/gui/sdl_gui.cpp b/src/gui/sdl_gui.cpp index d5d746eb..6258118a 100644 --- a/src/gui/sdl_gui.cpp +++ b/src/gui/sdl_gui.cpp @@ -40,7 +40,7 @@ extern Bit8u int10_font_14[256 * 14]; extern Program * first_shell; -extern void MSG_Write(const char *); +extern bool MSG_Write(const char *); extern void GFX_SetTitle(Bit32s cycles, Bits frameskip, bool paused); static int cursor, saved_bpp; diff --git a/src/gui/sdl_mapper.cpp b/src/gui/sdl_mapper.cpp index c905c052..ea541796 100644 --- a/src/gui/sdl_mapper.cpp +++ b/src/gui/sdl_mapper.cpp @@ -1584,6 +1584,9 @@ public: case MK_printscreen: key=SDLK_PRINT; break; + case MK_home: + key=SDLK_HOME; + break; } sprintf(buf,"%s \"key %d%s%s%s\"", entry, diff --git a/src/gui/sdlmain.cpp b/src/gui/sdlmain.cpp index 803a12f4..b5cdd332 100644 --- a/src/gui/sdlmain.cpp +++ b/src/gui/sdlmain.cpp @@ -989,6 +989,8 @@ static unsigned char logo[32*32*4]= { #include "dosbox_splash.h" //extern void UI_Run(bool); +void Restart(bool pressed); + static void GUI_StartUp(Section * sec) { sec->AddDestroyFunction(&GUI_ShutDown); Section_prop * section=static_cast(sec); @@ -1234,6 +1236,7 @@ static void GUI_StartUp(Section * sec) { MAPPER_AddHandler(KillSwitch,MK_f9,MMOD1,"shutdown","ShutDown"); MAPPER_AddHandler(CaptureMouse,MK_f10,MMOD1,"capmouse","Cap Mouse"); MAPPER_AddHandler(SwitchFullScreen,MK_return,MMOD2,"fullscr","Fullscreen"); + MAPPER_AddHandler(Restart,MK_home,MMOD1|MMOD2,"restart","Restart"); #if C_DEBUG /* Pause binds with activate-debugger */ #else @@ -1586,6 +1589,32 @@ static void launcheditor() { printf("can't find editor(s) specified at the command line.\n"); exit(1); } +#if C_DEBUG +extern void DEBUG_ShutDown(Section * /*sec*/); +#endif + +void restart_program(std::vector & parameters) { + char** newargs = new char* [parameters.size()+1]; + // parameter 0 is the executable path + // contents of the vector follow + // last one is NULL + for(Bitu i = 0; i < parameters.size(); i++) newargs[i]=(char*)parameters[i].c_str(); + newargs[parameters.size()] = NULL; + SDL_CloseAudio(); + SDL_Delay(50); + SDL_Quit(); +#if C_DEBUG + // shutdown curses + DEBUG_ShutDown(NULL); +#endif + + execvp(newargs[0], newargs); + free(newargs); +} +void Restart(bool pressed) { // mapper handler + restart_program(control->startup_params); +} + static void launchcaptures(std::string const& edit) { std::string path,file; Section* t = control->GetSection("dosbox"); @@ -1790,15 +1819,16 @@ int main(int argc, char* argv[]) { /* Parse configuration files */ std::string config_file,config_path; - bool parsed_anyconfigfile = false; - //First Parse -userconf + Cross::GetPlatformConfigDir(config_path); + + //First parse -userconf if(control->cmdline->FindExist("-userconf",true)){ config_file.clear(); Cross::GetPlatformConfigDir(config_path); Cross::GetPlatformConfigName(config_file); config_path += config_file; - if(control->ParseConfigFile(config_path.c_str())) parsed_anyconfigfile = true; - if(!parsed_anyconfigfile) { + control->ParseConfigFile(config_path.c_str()); + if(!control->configfiles.size()) { //Try to create the userlevel configfile. config_file.clear(); Cross::CreatePlatformConfigDir(config_path); @@ -1807,29 +1837,29 @@ int main(int argc, char* argv[]) { if(control->PrintConfig(config_path.c_str())) { LOG_MSG("CONFIG: Generating default configuration.\nWriting it to %s",config_path.c_str()); //Load them as well. Makes relative paths much easier - if(control->ParseConfigFile(config_path.c_str())) parsed_anyconfigfile = true; + control->ParseConfigFile(config_path.c_str()); } } } - //Second parse -conf entries - while(control->cmdline->FindString("-conf",config_file,true)) - if (control->ParseConfigFile(config_file.c_str())) parsed_anyconfigfile = true; + //Second parse -conf switches + while(control->cmdline->FindString("-conf",config_file,true)) { + if(!control->ParseConfigFile(config_file.c_str())) { + // try to load it from the user directory + control->ParseConfigFile((config_path + config_file).c_str()); + } + } + // if none found => parse localdir conf + if(!control->configfiles.size()) control->ParseConfigFile("dosbox.conf"); - //if none found => parse localdir conf - config_file = "dosbox.conf"; - if (!parsed_anyconfigfile && control->ParseConfigFile(config_file.c_str())) parsed_anyconfigfile = true; - - //if none found => parse userlevel conf - if(!parsed_anyconfigfile) { + // if none found => parse userlevel conf + if(!control->configfiles.size()) { config_file.clear(); - Cross::GetPlatformConfigDir(config_path); Cross::GetPlatformConfigName(config_file); - config_path += config_file; - if(control->ParseConfigFile(config_path.c_str())) parsed_anyconfigfile = true; + control->ParseConfigFile((config_path + config_file).c_str()); } - if(!parsed_anyconfigfile) { + if(!control->configfiles.size()) { //Try to create the userlevel configfile. config_file.clear(); Cross::CreatePlatformConfigDir(config_path); diff --git a/src/misc/cross.cpp b/src/misc/cross.cpp index bc4557e4..4d56293a 100644 --- a/src/misc/cross.cpp +++ b/src/misc/cross.cpp @@ -122,6 +122,19 @@ void Cross::CreateDir(std::string const& in) { #endif } +bool Cross::IsPathAbsolute(std::string const& in) { + // Absolute paths +#if defined (WIN32) || defined(OS2) + // drive letter + if (in.size() > 2 && in[1] == ':' ) return true; + // UNC path + else if (in.size() > 2 && in[0]=='\\' && in[1]=='\\') return true; +#else + if (in.size() > 1 && in[0] == '/' ) return true; +#endif + return false; +} + #if defined (WIN32) dir_information* open_directory(const char* dirname) { diff --git a/src/misc/messages.cpp b/src/misc/messages.cpp index b253d792..d6d7c6f9 100644 --- a/src/misc/messages.cpp +++ b/src/misc/messages.cpp @@ -124,13 +124,14 @@ const char * MSG_Get(char const * msg) { } -void MSG_Write(const char * location) { +bool MSG_Write(const char * location) { FILE* out=fopen(location,"w+t"); - if(out==NULL) return;//maybe an error? + if(out==NULL) return false;//maybe an error? for(itmb tel=Lang.begin();tel!=Lang.end();tel++){ fprintf(out,":%s\n%s\n.\n",(*tel).name.c_str(),(*tel).val.c_str()); } fclose(out); + return true; } void MSG_Init(Section_prop * section) { diff --git a/src/misc/programs.cpp b/src/misc/programs.cpp index f88f06ee..724d6a9b 100644 --- a/src/misc/programs.cpp +++ b/src/misc/programs.cpp @@ -19,6 +19,7 @@ /* $Id: programs.cpp,v 1.37 2009-05-27 09:15:42 qbix79 Exp $ */ #include +#include #include #include #include @@ -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 & 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(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 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 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 (sec); + if (psec==NULL) { + // failed; maybe it's the autoexec section? + Section_line* pline = dynamic_cast (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 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 (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 + (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 + (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 + (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."); } diff --git a/src/misc/setup.cpp b/src/misc/setup.cpp index 6c383366..a0184b49 100644 --- a/src/misc/setup.cpp +++ b/src/misc/setup.cpp @@ -29,6 +29,8 @@ #include #include #include +#include +#include using namespace std; static std::string current_config_dir; // Set by parseconfigfile so Prop_path can use it to construct the realpath @@ -104,7 +106,7 @@ bool Value::operator==(Value const& other) { } return false; } -void Value::SetValue(string const& in,Etype _type) throw(WrongType) { +bool Value::SetValue(string const& in,Etype _type) throw(WrongType) { /* Throw exception if the current type isn't the wanted type * Unless the wanted type is current. */ @@ -113,21 +115,22 @@ void Value::SetValue(string const& in,Etype _type) throw(WrongType) { if(type != V_NONE && type != _type) throw WrongType(); type = _type; } + bool retval = true; switch(type){ case V_HEX: - set_hex(in); + retval = set_hex(in); break; case V_INT: - set_int(in); + retval = set_int(in); break; case V_BOOL: - set_bool(in); + retval = set_bool(in); break; case V_STRING: set_string(in); break; case V_DOUBLE: - set_double(in); + retval = set_double(in); break; case V_NONE: @@ -137,39 +140,51 @@ void Value::SetValue(string const& in,Etype _type) throw(WrongType) { throw WrongType(); break; } + return retval; } -void Value::set_hex(std::string const& in) { +bool Value::set_hex(std::string const& in) { istringstream input(in); input.flags(ios::hex); - int result = 0; + Bits result = INT_MIN; input >> result; + if(result == INT_MIN) return false; _hex = result; + return true; } -void Value::set_int(string const &in) { +bool Value::set_int(string const &in) { istringstream input(in); - int result = 0; + Bits result = INT_MIN; input >> result; + if(result == INT_MIN) return false; _int = result; + return true; } -void Value::set_double(string const &in) { +bool Value::set_double(string const &in) { istringstream input(in); - double result = 0; + double result = std::numeric_limits::infinity(); input >> result; + if(result == std::numeric_limits::infinity()) return false; _double = result; + return true; } -void Value::set_bool(string const &in) { +bool Value::set_bool(string const &in) { istringstream input(in); string result; input >> result; - _bool = true; lowcase(result); - /* valid false entries: 0 ,d*, f*, off everything else gets true */ - if( !result.size() ) return; - if(result[0] == '0' || result[0] == 'd' || result[0] == 'f' || result == "off") + _bool = true; // TODO + if(!result.size()) return false; + + if(result=="0" || result=="disabled" || result=="false" || result=="off") { _bool = false; + } else if(result=="1" || result=="enabled" || result=="true" || result=="on") { + _bool = true; + } else return false; + + return true; } void Value::set_string(string const & in) { @@ -242,27 +257,30 @@ bool Prop_int::CheckValue(Value const& in, bool warn) { return false; } -void Prop_double::SetValue(std::string const& input){ - Value val(input,Value::V_DOUBLE); - SetVal(val,false,true); +bool Prop_double::SetValue(std::string const& input){ + Value val; + if(!val.SetValue(input,Value::V_DOUBLE)) return false; + return SetVal(val,false,true); } //void Property::SetValue(char* input){ // value.SetValue(input, Value::V_CURRENT); //} -void Prop_int::SetValue(std::string const& input){; - Value val(input,Value::V_INT); - SetVal(val,false,true); +bool Prop_int::SetValue(std::string const& input){; + Value val; + if(!val.SetValue(input,Value::V_INT)) return false; + bool retval = SetVal(val,false,true); + return retval; } -void Prop_string::SetValue(std::string const& input){ +bool Prop_string::SetValue(std::string const& input){ //Special version for lowcase stuff std::string temp(input); //suggested values always case insensitive. //If there are none then it can be paths and such which are case sensitive if(!suggested_values.empty()) lowcase(temp); Value val(temp,Value::V_STRING); - SetVal(val,false,true); + return SetVal(val,false,true); } bool Prop_string::CheckValue(Value const& in, bool warn){ if(suggested_values.empty()) return true; @@ -281,15 +299,15 @@ bool Prop_string::CheckValue(Value const& in, bool warn){ return false; } -void Prop_path::SetValue(std::string const& input){ +bool Prop_path::SetValue(std::string const& input){ //Special version to merge realpath with it Value val(input,Value::V_STRING); - SetVal(val,false,true); + bool retval = SetVal(val,false,true); if(input.empty()) { realpath = ""; - return; + return false; } std::string workcopy(input); Cross::ResolveHomedir(workcopy); //Parse ~ and friends @@ -297,20 +315,18 @@ void Prop_path::SetValue(std::string const& input){ if( current_config_dir.empty()) realpath = workcopy; else realpath = current_config_dir + CROSS_FILESPLIT + workcopy; //Absolute paths -#if defined (WIN32) || defined(OS2) - if( workcopy.size() > 2 && workcopy[1] == ':' ) realpath = workcopy; -#else - if( workcopy.size() > 1 && workcopy[0] == '/' ) realpath = workcopy; -#endif + if (Cross::IsPathAbsolute(workcopy)) realpath = workcopy; + return retval; } -void Prop_bool::SetValue(std::string const& input){ - value.SetValue(input,Value::V_BOOL); +bool Prop_bool::SetValue(std::string const& input){ + return value.SetValue(input,Value::V_BOOL); } -void Prop_hex::SetValue(std::string const& input){ - Value val(input,Value::V_HEX); - SetVal(val,false,true); +bool Prop_hex::SetValue(std::string const& input){ + Value val; + val.SetValue(input,Value::V_HEX); + return SetVal(val,false,true); } void Prop_multival::make_default_value(){ @@ -331,15 +347,15 @@ void Prop_multival::make_default_value(){ //TODO checkvalue stuff -void Prop_multival_remain::SetValue(std::string const& input) { +bool Prop_multival_remain::SetValue(std::string const& input) { Value val(input,Value::V_STRING); - SetVal(val,false,true); + bool retval = SetVal(val,false,true); std::string local(input); int i = 0,number_of_properties = 0; Property *p = section->Get_prop(0); //No properties in this section. do nothing - if(!p) return; + if(!p) return false; while( (section->Get_prop(number_of_properties)) ) number_of_properties++; @@ -364,22 +380,23 @@ void Prop_multival_remain::SetValue(std::string const& input) { Value valtest (in,p->Get_type()); if(!p->CheckValue(valtest,true)) { make_default_value(); - return; + return false; } p->SetValue(in); } + return retval; } //TODO checkvalue stuff -void Prop_multival::SetValue(std::string const& input) { +bool Prop_multival::SetValue(std::string const& input) { Value val(input,Value::V_STRING); - SetVal(val,false,true); + bool retval = SetVal(val,false,true); std::string local(input); int i = 0; Property *p = section->Get_prop(0); //No properties in this section. do nothing - if(!p) return; + if(!p) return false; string::size_type loc = string::npos; while( (p = section->Get_prop(i++)) ) { //trim leading seperators @@ -398,11 +415,12 @@ void Prop_multival::SetValue(std::string const& input) { Value valtest (in,p->Get_type()); if(!p->CheckValue(valtest,true)) { make_default_value(); - return; + return false; } p->SetValue(in); } + return retval; } const std::vector& Property::GetValues() const { @@ -563,20 +581,20 @@ void trim(string& in) { } //TODO double c_str -void Section_prop::HandleInputline(string const& gegevens){ +bool Section_prop::HandleInputline(string const& gegevens){ string str1 = gegevens; string::size_type loc = str1.find('='); - if(loc == string::npos) return; + if(loc == string::npos) return false; string name = str1.substr(0,loc); string val = str1.substr(loc + 1); /* trim the results incase there were spaces somewhere */ trim(name);trim(val); for(it tel=properties.begin();tel!=properties.end();tel++){ if(!strcasecmp((*tel)->propname.c_str(),name.c_str())){ - (*tel)->SetValue(val); - return; + return (*tel)->SetValue(val); } } + return false; } void Section_prop::PrintData(FILE* outfile) const { @@ -596,9 +614,10 @@ string Section_prop::GetPropValue(string const& _property) const{ return NO_SUCH_PROPERTY; } -void Section_line::HandleInputline(string const& line){ +bool Section_line::HandleInputline(string const& line){ data+=line; data+="\n"; + return true; } void Section_line::PrintData(FILE* outfile) const { @@ -770,11 +789,13 @@ Section* Config::GetSectionFromProperty(char const * const prop) const{ bool Config::ParseConfigFile(char const * const configfilename){ - static bool first_configfile = true; + //static bool first_configfile = true; ifstream in(configfilename); if (!in) return false; - const char * settings_type = first_configfile?"primary":"additional"; - first_configfile = false; + const char * settings_type; + settings_type = (configfiles.size() == 0)? "primary":"additional"; + configfiles.push_back(configfilename); + LOG_MSG("CONFIG:Loading %s settings from config file %s", settings_type,configfilename); //Get directory from configfilename, used with relative paths. @@ -824,6 +845,10 @@ bool Config::ParseConfigFile(char const * const configfilename){ return true; } +/*const char* Config::GetPrimaryConfigFile() { + return configfile.c_str(); +}*/ + void Config::ParseEnv(char ** envp) { for(char** env=envp; *env;env++) { char copy[1024]; @@ -850,6 +875,7 @@ void Config::SetStartUp(void (*_function)(void)) { void Config::StartUp(void) { + initialised=true; (*_start_function)(); } @@ -946,6 +972,90 @@ unsigned int CommandLine::GetCount(void) { return (unsigned int)cmds.size(); } +void CommandLine::FillVector(std::vector & vector) { + for(cmd_it it=cmds.begin(); it != cmds.end(); it++) { + vector.push_back((*it)); + } + // add back the \" if the parameter contained a space + for(Bitu i = 0; i < vector.size(); i++) { + if(vector[i].find(' ') != std::string::npos) { + vector[i] = "\""+vector[i]+"\""; + } + } +} + +int CommandLine::GetParameterFromList(const char* const params[], std::vector & output) { + // return values: 0 = P_NOMATCH, 1 = P_NOPARAMS + // TODO return nomoreparams + int retval = 1; + output.clear(); + enum { + P_START, P_FIRSTNOMATCH, P_FIRSTMATCH + } parsestate = P_START; + cmd_it it = cmds.begin(); + while(it!=cmds.end()) { + bool found = false; + for(Bitu i = 0; params[i]!=""; i++) { + if (!strcasecmp((*it).c_str(),params[i])) { + // found a parameter + found = true; + switch(parsestate) { + case P_START: + retval = i+2; + parsestate = P_FIRSTMATCH; + break; + case P_FIRSTMATCH: + case P_FIRSTNOMATCH: + return retval; + } + } + } + if(!found) + switch(parsestate) { + case P_START: + retval = 0; // no match + parsestate = P_FIRSTNOMATCH; + output.push_back(*it); + break; + case P_FIRSTMATCH: + case P_FIRSTNOMATCH: + output.push_back(*it); + break; + } + cmd_it itold = it; + it++; + cmds.erase(itold); + + } + + return retval; +/* +bool CommandLine::FindEntry(char const * const name,cmd_it & it,bool neednext) { + for (it=cmds.begin();it!=cmds.end();it++) { + if (!strcasecmp((*it).c_str(),name)) { + cmd_it itnext=it;itnext++; + if (neednext && (itnext==cmds.end())) return false; + return true; + } + } + return false; +*/ + + +/* + cmd_it it=cmds.begin();value=(*it++); + while(it != cmds.end()) { + if(params. + + it++; + } +*/ + // find next parameter + //return -1; + +} + + CommandLine::CommandLine(int argc,char const * const argv[]) { if (argc>0) { file_name=argv[0];