DOSBox-X
|
00001 /* 00002 * Copyright (C) 2002-2020 The DOSBox Team 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License along 00015 * with this program; if not, write to the Free Software Foundation, Inc., 00016 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 */ 00018 00019 00020 #include <vector> 00021 #include <sstream> 00022 #include <ctype.h> 00023 #include <assert.h> 00024 #include <stdlib.h> 00025 #include <stdarg.h> 00026 #include <stdio.h> 00027 #include <string.h> 00028 #include "programs.h" 00029 #include "callback.h" 00030 #include "regs.h" 00031 #include "support.h" 00032 #include "cross.h" 00033 #include "control.h" 00034 #include "shell.h" 00035 #include "menu.h" 00036 00037 Bitu call_program; 00038 00039 extern int enablelfn, paste_speed, wheel_key; 00040 extern const char *modifier; 00041 extern bool dos_kernel_disabled, force_nocachedir, freesizecap, wpcolon; 00042 00043 /* This registers a file on the virtual drive and creates the correct structure for it*/ 00044 00045 static Bit8u exe_block[]={ 00046 0xbc,0x00,0x04, //0x100 MOV SP,0x400 decrease stack size 00047 0xbb,0x40,0x00, //0x103 MOV BX,0x0040 for memory resize 00048 0xb4,0x4a, //0x106 MOV AH,0x4A Resize memory block 00049 0xcd,0x21, //0x108 INT 0x21 ... 00050 0x30,0xc0, //0x10A XOR AL,AL Clear AL (exit code). Program will write AL to modify exit status 00051 //pos 14 is callback number 00052 0xFE,0x38,0x00,0x00, //0x10C CALLBack number 00053 0xb4,0x4c, //0x110 Mov AH,0x4C Prepare to exit, preserve AL 00054 0xcd,0x21 //0x112 INT 0x21 Exit to DOS 00055 }; //0x114 --DONE-- 00056 00057 #define CB_POS 14 00058 00059 class InternalProgramEntry { 00060 public: 00061 InternalProgramEntry() { 00062 main = NULL; 00063 comsize = 0; 00064 comdata = NULL; 00065 } 00066 ~InternalProgramEntry() { 00067 if (comdata != NULL) free(comdata); 00068 comdata = NULL; 00069 comsize = 0; 00070 main = NULL; 00071 } 00072 public: 00073 std::string name; 00074 Bit8u* comdata; 00075 Bit32u comsize; 00076 PROGRAMS_Main* main; 00077 }; 00078 00079 static std::vector<InternalProgramEntry*> internal_progs; 00080 00081 void PROGRAMS_Shutdown(void) { 00082 LOG(LOG_MISC,LOG_DEBUG)("Shutting down internal programs list"); 00083 00084 for (size_t i=0;i < internal_progs.size();i++) { 00085 if (internal_progs[i] != NULL) { 00086 delete internal_progs[i]; 00087 internal_progs[i] = NULL; 00088 } 00089 } 00090 internal_progs.clear(); 00091 } 00092 00093 void PROGRAMS_MakeFile(char const * const name,PROGRAMS_Main * main) { 00094 Bit32u size=sizeof(exe_block)+sizeof(Bit8u); 00095 InternalProgramEntry *ipe; 00096 Bit8u *comdata; 00097 Bit8u index; 00098 00099 /* Copy save the pointer in the vector and save it's index */ 00100 if (internal_progs.size()>255) E_Exit("PROGRAMS_MakeFile program size too large (%d)",static_cast<int>(internal_progs.size())); 00101 00102 index = (Bit8u)internal_progs.size(); 00103 comdata = (Bit8u *)malloc(32); //MEM LEAK 00104 memcpy(comdata,&exe_block,sizeof(exe_block)); 00105 memcpy(&comdata[sizeof(exe_block)],&index,sizeof(index)); 00106 comdata[CB_POS]=(Bit8u)(call_program&0xff); 00107 comdata[CB_POS+1]=(Bit8u)((call_program>>8)&0xff); 00108 00109 ipe = new InternalProgramEntry(); 00110 ipe->main = main; 00111 ipe->name = name; 00112 ipe->comsize = size; 00113 ipe->comdata = comdata; 00114 internal_progs.push_back(ipe); 00115 VFILE_Register(name,ipe->comdata,ipe->comsize); 00116 } 00117 00118 static Bitu PROGRAMS_Handler(void) { 00119 /* This sets up everything for a program start up call */ 00120 Bitu size=sizeof(Bit8u); 00121 Bit8u index; 00122 /* Read the index from program code in memory */ 00123 PhysPt reader=PhysMake(dos.psp(),256+sizeof(exe_block)); 00124 HostPt writer=(HostPt)&index; 00125 for (;size>0;size--) *writer++=mem_readb(reader++); 00126 Program * new_program = NULL; 00127 if (index >= internal_progs.size()) E_Exit("something is messing with the memory"); 00128 InternalProgramEntry *ipe = internal_progs[index]; 00129 if (ipe == NULL) E_Exit("Attempt to run internal program slot with nothing allocated"); 00130 if (ipe->main == NULL) return CBRET_NONE; 00131 PROGRAMS_Main * handler = internal_progs[index]->main; 00132 (*handler)(&new_program); 00133 00134 try { /* "BOOT" can throw an exception (int(2)) */ 00135 new_program->Run(); 00136 delete new_program; 00137 } 00138 catch (...) { /* well if it happened, free the program anyway to avert memory leaks */ 00139 delete new_program; 00140 throw; /* pass it on */ 00141 } 00142 00143 return CBRET_NONE; 00144 } 00145 00146 /* Main functions used in all program */ 00147 00148 Program::Program() { 00149 /* Find the command line and setup the PSP */ 00150 psp = new DOS_PSP(dos.psp()); 00151 /* Scan environment for filename */ 00152 PhysPt envscan=PhysMake(psp->GetEnvironment(),0); 00153 while (mem_readb(envscan)) envscan+=(PhysPt)(mem_strlen(envscan)+1); 00154 envscan+=3; 00155 CommandTail tail; 00156 MEM_BlockRead(PhysMake(dos.psp(),CTBUF+1),&tail,CTBUF+1); 00157 if (tail.count<CTBUF) tail.buffer[tail.count]=0; 00158 else tail.buffer[CTBUF-1]=0; 00159 char filename[256+1]; 00160 MEM_StrCopy(envscan,filename,256); 00161 cmd = new CommandLine(filename,tail.buffer); 00162 exit_status = 0; 00163 } 00164 00165 extern std::string full_arguments; 00166 00167 void Program::WriteExitStatus() { 00168 /* the exe block was modified that on return to DOS only AH is modified, leaving AL normally 00169 * zero but we're free to set AL to any other value to set exit code */ 00170 reg_al = exit_status; 00171 } 00172 00173 void Program::ChangeToLongCmd() { 00174 /* 00175 * Get arguments directly from the shell instead of the psp. 00176 * this is done in securemode: (as then the arguments to mount and friends 00177 * can only be given on the shell ( so no int 21 4b) 00178 * Securemode part is disabled as each of the internal command has already 00179 * protection for it. (and it breaks games like cdman) 00180 * it is also done for long arguments to as it is convient (as the total commandline can be longer then 127 characters. 00181 * imgmount with lot's of parameters 00182 * Length of arguments can be ~120. but switch when above 100 to be sure 00183 */ 00184 00185 if (/*control->SecureMode() ||*/ cmd->Get_arglength() > 100) { 00186 CommandLine* temp = new CommandLine(cmd->GetFileName(),full_arguments.c_str()); 00187 delete cmd; 00188 cmd = temp; 00189 } 00190 full_arguments.assign(""); //Clear so it gets even more save 00191 } 00192 00193 static char last_written_character = 0;//For 0xA to OxD 0xA expansion 00194 void Program::WriteOut(const char * format,...) { 00195 char buf[2048]; 00196 va_list msg; 00197 00198 va_start(msg,format); 00199 vsnprintf(buf,2047,format,msg); 00200 va_end(msg); 00201 00202 Bit16u size = (Bit16u)strlen(buf); 00203 dos.internal_output=true; 00204 for(Bit16u i = 0; i < size;i++) { 00205 Bit8u out;Bit16u s=1; 00206 if (buf[i] == 0xA && last_written_character != 0xD) { 00207 out = 0xD;DOS_WriteFile(STDOUT,&out,&s); 00208 } 00209 last_written_character = (char)(out = (Bit8u)buf[i]); 00210 DOS_WriteFile(STDOUT,&out,&s); 00211 } 00212 dos.internal_output=false; 00213 00214 // DOS_WriteFile(STDOUT,(Bit8u *)buf,&size); 00215 } 00216 00217 void Program::WriteOut_NoParsing(const char * format) { 00218 Bit16u size = (Bit16u)strlen(format); 00219 char const* buf = format; 00220 dos.internal_output=true; 00221 for(Bit16u i = 0; i < size;i++) { 00222 Bit8u out;Bit16u s=1; 00223 if (buf[i] == 0xA && last_written_character != 0xD) { 00224 out = 0xD;DOS_WriteFile(STDOUT,&out,&s); 00225 } 00226 last_written_character = (char)(out = (Bit8u)buf[i]); 00227 DOS_WriteFile(STDOUT,&out,&s); 00228 } 00229 dos.internal_output=false; 00230 00231 // DOS_WriteFile(STDOUT,(Bit8u *)format,&size); 00232 } 00233 00234 static bool LocateEnvironmentBlock(PhysPt &env_base,PhysPt &env_fence,Bitu env_seg) { 00235 if (env_seg == 0) { 00236 /* The DOS program might have freed it's environment block perhaps. */ 00237 return false; 00238 } 00239 00240 DOS_MCB env_mcb((Bit16u)(env_seg-1)); /* read the environment block's MCB to determine how large it is */ 00241 env_base = PhysMake((Bit16u)env_seg,0); 00242 env_fence = env_base + (PhysPt)(env_mcb.GetSize() << 4u); 00243 return true; 00244 } 00245 00246 int EnvPhys_StrCmp(PhysPt es,PhysPt ef,const char *ls) { 00247 (void)ef;//UNUSED 00248 while (1) { 00249 unsigned char a = mem_readb(es++); 00250 unsigned char b = (unsigned char)(*ls++); 00251 if (a == '=') a = 0; 00252 if (a == 0 && b == 0) break; 00253 if (a == b) continue; 00254 return (int)a - (int)b; 00255 } 00256 00257 return 0; 00258 } 00259 00260 void EnvPhys_StrCpyToCPPString(std::string &result,PhysPt &env_scan,PhysPt env_fence) { 00261 char tmp[512],*w=tmp,*wf=tmp+sizeof(tmp)-1; 00262 00263 result.clear(); 00264 while (env_scan < env_fence) { 00265 char c; 00266 if ((c=(char)mem_readb(env_scan++)) == 0) break; 00267 00268 if (w >= wf) { 00269 *w = 0; 00270 result += tmp; 00271 w = tmp; 00272 } 00273 00274 assert(w < wf); 00275 *w++ = c; 00276 } 00277 if (w != tmp) { 00278 *w = 0; 00279 result += tmp; 00280 } 00281 } 00282 00283 bool EnvPhys_ScanUntilNextString(PhysPt &env_scan,PhysPt env_fence) { 00284 /* scan until end of block or NUL */ 00285 while (env_scan < env_fence && mem_readb(env_scan) != 0) env_scan++; 00286 00287 /* if we hit the fence, that's something to warn about. */ 00288 if (env_scan >= env_fence) { 00289 LOG_MSG("Warning: environment string scan hit the end of the environment block without terminating NUL\n"); 00290 return false; 00291 } 00292 00293 /* if we stopped at anything other than a NUL, that's something to warn about */ 00294 if (mem_readb(env_scan) != 0) { 00295 LOG_MSG("Warning: environment string scan scan stopped without hitting NUL\n"); 00296 return false; 00297 } 00298 00299 env_scan++; /* skip NUL */ 00300 return true; 00301 } 00302 00303 bool Program::GetEnvStr(const char * entry,std::string & result) { 00304 PhysPt env_base,env_fence,env_scan; 00305 00306 if (dos_kernel_disabled) { 00307 LOG_MSG("BUG: Program::GetEnvNum() called with DOS kernel disabled (such as OS boot).\n"); 00308 return false; 00309 } 00310 00311 if (!LocateEnvironmentBlock(env_base,env_fence,psp->GetEnvironment())) { 00312 LOG_MSG("Warning: GetEnvCount() was not able to locate the program's environment block\n"); 00313 return false; 00314 } 00315 00316 std::string bigentry(entry); 00317 for (std::string::iterator it = bigentry.begin(); it != bigentry.end(); ++it) *it = toupper(*it); 00318 00319 env_scan = env_base; 00320 while (env_scan < env_fence) { 00321 /* "NAME" + "=" + "VALUE" + "\0" */ 00322 /* end of the block is a NULL string meaning a \0 follows the last string's \0 */ 00323 if (mem_readb(env_scan) == 0) break; /* normal end of block */ 00324 00325 if (EnvPhys_StrCmp(env_scan,env_fence,bigentry.c_str()) == 0) { 00326 EnvPhys_StrCpyToCPPString(result,env_scan,env_fence); 00327 return true; 00328 } 00329 00330 if (!EnvPhys_ScanUntilNextString(env_scan,env_fence)) break; 00331 } 00332 00333 return false; 00334 } 00335 00336 bool Program::GetEnvNum(Bitu want_num,std::string & result) { 00337 PhysPt env_base,env_fence,env_scan; 00338 Bitu num = 0; 00339 00340 if (dos_kernel_disabled) { 00341 LOG_MSG("BUG: Program::GetEnvNum() called with DOS kernel disabled (such as OS boot).\n"); 00342 return false; 00343 } 00344 00345 if (!LocateEnvironmentBlock(env_base,env_fence,psp->GetEnvironment())) { 00346 LOG_MSG("Warning: GetEnvCount() was not able to locate the program's environment block\n"); 00347 return false; 00348 } 00349 00350 result.clear(); 00351 env_scan = env_base; 00352 while (env_scan < env_fence) { 00353 /* "NAME" + "=" + "VALUE" + "\0" */ 00354 /* end of the block is a NULL string meaning a \0 follows the last string's \0 */ 00355 if (mem_readb(env_scan) == 0) break; /* normal end of block */ 00356 00357 if (num == want_num) { 00358 EnvPhys_StrCpyToCPPString(result,env_scan,env_fence); 00359 return true; 00360 } 00361 00362 num++; 00363 if (!EnvPhys_ScanUntilNextString(env_scan,env_fence)) break; 00364 } 00365 00366 return false; 00367 } 00368 00369 Bitu Program::GetEnvCount(void) { 00370 PhysPt env_base,env_fence,env_scan; 00371 Bitu num = 0; 00372 00373 if (dos_kernel_disabled) { 00374 LOG_MSG("BUG: Program::GetEnvCount() called with DOS kernel disabled (such as OS boot).\n"); 00375 return 0; 00376 } 00377 00378 if (!LocateEnvironmentBlock(env_base,env_fence,psp->GetEnvironment())) { 00379 LOG_MSG("Warning: GetEnvCount() was not able to locate the program's environment block\n"); 00380 return false; 00381 } 00382 00383 env_scan = env_base; 00384 while (env_scan < env_fence) { 00385 /* "NAME" + "=" + "VALUE" + "\0" */ 00386 /* end of the block is a NULL string meaning a \0 follows the last string's \0 */ 00387 if (mem_readb(env_scan++) == 0) break; /* normal end of block */ 00388 num++; 00389 if (!EnvPhys_ScanUntilNextString(env_scan,env_fence)) break; 00390 } 00391 00392 return num; 00393 } 00394 00395 void Program::DebugDumpEnv() { 00396 PhysPt env_base,env_fence,env_scan; 00397 unsigned char c; 00398 std::string tmp; 00399 00400 if (dos_kernel_disabled) 00401 return; 00402 00403 if (!LocateEnvironmentBlock(env_base,env_fence,psp->GetEnvironment())) 00404 return; 00405 00406 env_scan = env_base; 00407 LOG_MSG("DebugDumpEnv()"); 00408 while (env_scan < env_fence) { 00409 if (mem_readb(env_scan) == 0) break; 00410 00411 while (env_scan < env_fence) { 00412 if ((c=mem_readb(env_scan++)) == 0) break; 00413 tmp += (char)c; 00414 } 00415 00416 LOG_MSG("...%s",tmp.c_str()); 00417 tmp = ""; 00418 } 00419 } 00420 00421 /* NTS: "entry" string must have already been converted to uppercase */ 00422 bool Program::SetEnv(const char * entry,const char * new_string) { 00423 PhysPt env_base,env_fence,env_scan; 00424 size_t nsl = 0,el = 0,needs; 00425 00426 if (dos_kernel_disabled) { 00427 LOG_MSG("BUG: Program::SetEnv() called with DOS kernel disabled (such as OS boot).\n"); 00428 return false; 00429 } 00430 00431 if (!LocateEnvironmentBlock(env_base,env_fence,psp->GetEnvironment())) { 00432 LOG_MSG("Warning: SetEnv() was not able to locate the program's environment block\n"); 00433 return false; 00434 } 00435 00436 std::string bigentry(entry); 00437 for (std::string::iterator it = bigentry.begin(); it != bigentry.end(); ++it) *it = toupper(*it); 00438 00439 el = strlen(bigentry.c_str()); 00440 if (*new_string != 0) nsl = strlen(new_string); 00441 needs = nsl+1+el+1+1; /* entry + '=' + new_string + '\0' + '\0' */ 00442 00443 /* look for the variable in the block. break the loop if found */ 00444 env_scan = env_base; 00445 while (env_scan < env_fence) { 00446 if (mem_readb(env_scan) == 0) break; 00447 00448 if (EnvPhys_StrCmp(env_scan,env_fence,bigentry.c_str()) == 0) { 00449 /* found it. remove by shifting the rest of the environment block over */ 00450 int zeroes=0; 00451 PhysPt s,d; 00452 00453 /* before we remove it: is there room for the new value? */ 00454 if (nsl != 0) { 00455 if ((env_scan+needs) > env_fence) { 00456 LOG_MSG("Program::SetEnv() error, insufficient room for environment variable %s=%s (replacement)\n",bigentry.c_str(),new_string); 00457 DebugDumpEnv(); 00458 return false; 00459 } 00460 } 00461 00462 s = env_scan; d = env_scan; 00463 while (s < env_fence && mem_readb(s) != 0) s++; 00464 if (s < env_fence && mem_readb(s) == 0) s++; 00465 00466 while (s < env_fence) { 00467 unsigned char b = mem_readb(s++); 00468 00469 if (b == 0) zeroes++; 00470 else zeroes=0; 00471 00472 mem_writeb(d++,b); 00473 if (zeroes >= 2) break; /* two consecutive zeros means the end of the block */ 00474 } 00475 } 00476 else { 00477 /* scan to next string */ 00478 if (!EnvPhys_ScanUntilNextString(env_scan,env_fence)) break; 00479 } 00480 } 00481 00482 /* At this point, env_scan points to the first byte beyond the block */ 00483 /* add the string to the end of the block */ 00484 if (*new_string != 0) { 00485 if ((env_scan+needs) > env_fence) { 00486 LOG_MSG("Program::SetEnv() error, insufficient room for environment variable %s=%s (addition)\n",bigentry.c_str(),new_string); 00487 DebugDumpEnv(); 00488 return false; 00489 } 00490 00491 assert(env_scan < env_fence); 00492 for (const char *s=bigentry.c_str();*s != 0;) mem_writeb(env_scan++,(Bit8u)(*s++)); 00493 mem_writeb(env_scan++,'='); 00494 00495 assert(env_scan < env_fence); 00496 for (const char *s=new_string;*s != 0;) mem_writeb(env_scan++,(Bit8u)(*s++)); 00497 mem_writeb(env_scan++,0); 00498 mem_writeb(env_scan++,0); 00499 00500 assert(env_scan <= env_fence); 00501 } 00502 00503 return true; 00504 } 00505 00506 bool MSG_Write(const char *); 00507 00513 class CONFIG : public Program { 00514 public: 00517 void Run(void); 00518 private: 00519 void restart(const char* useconfig); 00520 00521 void writeconf(std::string name, bool configdir,bool everything) { 00522 // "config -wcd" should write to the config directory 00523 if (configdir) { 00524 // write file to the default config directory 00525 std::string config_path; 00526 Cross::GetPlatformConfigDir(config_path); 00527 struct stat info; 00528 if (!stat(config_path.c_str(), &info) || !(info.st_mode & S_IFDIR)) { 00529 #ifdef WIN32 00530 CreateDirectory(config_path.c_str(), NULL); 00531 #else 00532 mkdir(config_path.c_str(), 0755); 00533 #endif 00534 } 00535 name = config_path + name; 00536 } 00537 WriteOut(MSG_Get("PROGRAM_CONFIG_FILE_WHICH"),name.c_str()); 00538 if (!control->PrintConfig(name.c_str(),everything)) { 00539 WriteOut(MSG_Get("PROGRAM_CONFIG_FILE_ERROR"),name.c_str()); 00540 } 00541 return; 00542 } 00543 00544 bool securemode_check() { 00545 if (control->SecureMode()) { 00546 WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW")); 00547 return true; 00548 } 00549 return false; 00550 } 00551 }; 00552 00553 void ReloadMapper(Section_prop *sec, bool init); 00554 void CONFIG::Run(void) { 00555 static const char* const params[] = { 00556 "-r", "-wcp", "-wcd", "-wc", "-writeconf", "-l", "-rmconf", 00557 "-h", "-help", "-?", "-axclear", "-axadd", "-axtype", "-get", "-set", 00558 "-writelang", "-wl", "-securemode", "-all", "-errtest", NULL }; 00559 enum prs { 00560 P_NOMATCH, P_NOPARAMS, // fixed return values for GetParameterFromList 00561 P_RESTART, 00562 P_WRITECONF_PORTABLE, P_WRITECONF_DEFAULT, P_WRITECONF, P_WRITECONF2, 00563 P_LISTCONF, P_KILLCONF, 00564 P_HELP, P_HELP2, P_HELP3, 00565 P_AUTOEXEC_CLEAR, P_AUTOEXEC_ADD, P_AUTOEXEC_TYPE, 00566 P_GETPROP, P_SETPROP, 00567 P_WRITELANG, P_WRITELANG2, 00568 P_SECURE, P_ALL, P_ERRTEST 00569 } presult = P_NOMATCH; 00570 00571 bool all = false; 00572 bool first = true; 00573 std::vector<std::string> pvars; 00574 if (cmd->FindExist("-all", true)) all = true; 00575 // Loop through the passed parameters 00576 while(presult != P_NOPARAMS) { 00577 presult = (enum prs)cmd->GetParameterFromList(params, pvars); 00578 switch(presult) { 00579 00580 case P_ALL: 00581 all = true; 00582 break; 00583 00584 case P_ERRTEST: 00585 exit_status = 1; 00586 WriteExitStatus(); 00587 return; 00588 00589 case P_RESTART: 00590 WriteOut("-restart is no longer supported\n"); 00591 return; 00592 00593 case P_LISTCONF: { 00594 Bitu size = (Bitu)control->configfiles.size(); 00595 std::string config_path; 00596 Cross::GetPlatformConfigDir(config_path); 00597 WriteOut(MSG_Get("PROGRAM_CONFIG_CONFDIR"), VERSION,config_path.c_str()); 00598 if (size==0) WriteOut(MSG_Get("PROGRAM_CONFIG_NOCONFIGFILE")); 00599 else { 00600 WriteOut(MSG_Get("PROGRAM_CONFIG_PRIMARY_CONF"),control->configfiles.front().c_str()); 00601 if (size > 1) { 00602 WriteOut(MSG_Get("PROGRAM_CONFIG_ADDITIONAL_CONF")); 00603 for(Bitu i = 1; i < size; i++) 00604 WriteOut("%s\n",control->configfiles[i].c_str()); 00605 } 00606 } 00607 if (control->startup_params.size() > 0) { 00608 std::string test; 00609 for(size_t k = 0; k < control->startup_params.size(); k++) 00610 test += control->startup_params[k] + " "; 00611 WriteOut(MSG_Get("PROGRAM_CONFIG_PRINT_STARTUP"), test.c_str()); 00612 } 00613 break; 00614 } 00615 case P_WRITECONF: case P_WRITECONF2: 00616 if (securemode_check()) return; 00617 if (pvars.size() > 1) return; 00618 else if (pvars.size() == 1) { 00619 // write config to specific file, except if it is an absolute path 00620 writeconf(pvars[0], !Cross::IsPathAbsolute(pvars[0]), all); 00621 } else { 00622 // -wc without parameter: write primary config file 00623 if (control->configfiles.size()) writeconf(control->configfiles[0], false, all); 00624 else WriteOut(MSG_Get("PROGRAM_CONFIG_NOCONFIGFILE")); 00625 } 00626 break; 00627 case P_WRITECONF_DEFAULT: { 00628 // write to /userdir/dosbox-x-0.xx.conf 00629 if (securemode_check()) return; 00630 if (pvars.size() > 0) return; 00631 std::string confname; 00632 Cross::GetPlatformConfigName(confname); 00633 writeconf(confname, true, all); 00634 break; 00635 } 00636 case P_WRITECONF_PORTABLE: 00637 if (securemode_check()) return; 00638 if (pvars.size() > 1) return; 00639 else if (pvars.size() == 1) { 00640 // write config to startup directory 00641 writeconf(pvars[0], false, all); 00642 } else { 00643 // -wcp without parameter: write dosbox-x.conf to startup directory 00644 writeconf(std::string("dosbox-x.conf"), false, all); 00645 } 00646 break; 00647 00648 case P_NOPARAMS: 00649 if (!first) break; 00650 00651 case P_NOMATCH: 00652 WriteOut(MSG_Get("PROGRAM_CONFIG_USAGE")); 00653 return; 00654 00655 case P_HELP: case P_HELP2: case P_HELP3: { 00656 switch(pvars.size()) { 00657 case 0: 00658 WriteOut(MSG_Get("PROGRAM_CONFIG_USAGE")); 00659 return; 00660 case 1: { 00661 if (!strcasecmp("sections",pvars[0].c_str())) { 00662 // list the sections 00663 WriteOut(MSG_Get("PROGRAM_CONFIG_HLP_SECTLIST")); 00664 int i = 0; 00665 while(true) { 00666 Section* sec = control->GetSection(i++); 00667 if (!sec) break; 00668 WriteOut("%s\n",sec->GetName()); 00669 } 00670 return; 00671 } 00672 // if it's a section, leave it as one-param 00673 Section* sec = control->GetSection(pvars[0].c_str()); 00674 if (!sec) { 00675 // could be a property 00676 sec = control->GetSectionFromProperty(pvars[0].c_str()); 00677 if (!sec) { 00678 WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR")); 00679 return; 00680 } 00681 pvars.insert(pvars.begin(),std::string(sec->GetName())); 00682 } 00683 break; 00684 } 00685 case 2: { 00686 // sanity check 00687 Section* sec = control->GetSection(pvars[0].c_str()); 00688 Section* sec2 = control->GetSectionFromProperty(pvars[1].c_str()); 00689 00690 if (sec != sec2) { 00691 WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_DUPLICATE")); 00692 } 00693 break; 00694 } 00695 default: 00696 WriteOut(MSG_Get("PROGRAM_CONFIG_USAGE")); 00697 return; 00698 } 00699 // if we have one value in pvars, it's a section 00700 // two values are section + property 00701 Section* sec = control->GetSection(pvars[0].c_str()); 00702 if (sec==NULL) { 00703 WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR")); 00704 return; 00705 } 00706 Section_prop* psec = dynamic_cast <Section_prop*>(sec); 00707 if (psec==NULL) { 00708 // failed; maybe it's the autoexec section? 00709 Section_line* pline = dynamic_cast <Section_line*>(sec); 00710 if (pline==NULL) E_Exit("Section dynamic cast failed."); 00711 00712 WriteOut(MSG_Get("PROGRAM_CONFIG_HLP_LINEHLP"), 00713 pline->GetName(), 00714 // this is 'unclean' but the autoexec section has no help associated 00715 MSG_Get("AUTOEXEC_CONFIGFILE_HELP"), 00716 pline->data.c_str() ); 00717 return; 00718 } 00719 if (pvars.size()==1) { 00720 size_t i = 0; 00721 WriteOut(MSG_Get("PROGRAM_CONFIG_HLP_SECTHLP"),pvars[0].c_str()); 00722 while(true) { 00723 // list the properties 00724 Property* p = psec->Get_prop((int)(i++)); 00725 if (p==NULL) break; 00726 WriteOut("%s\n", p->propname.c_str()); 00727 } 00728 if (!strcasecmp(pvars[0].c_str(), "config")) 00729 WriteOut("set\ninstall\ninstallhigh\ndevice\ndevicehigh\n"); 00730 } else { 00731 // find the property by it's name 00732 size_t i = 0; 00733 while (true) { 00734 Property *p = psec->Get_prop((int)(i++)); 00735 if (p==NULL) break; 00736 if (!strcasecmp(p->propname.c_str(),pvars[1].c_str())) { 00737 // found it; make the list of possible values 00738 std::string propvalues; 00739 std::vector<Value> pv = p->GetValues(); 00740 00741 if (p->Get_type()==Value::V_BOOL) { 00742 // possible values for boolean are true, false 00743 propvalues += "true, false"; 00744 } else if (p->Get_type()==Value::V_INT) { 00745 // print min, max for integer values if used 00746 Prop_int* pint = dynamic_cast <Prop_int*>(p); 00747 if (pint==NULL) E_Exit("Int property dynamic cast failed."); 00748 if (pint->getMin() != pint->getMax()) { 00749 std::ostringstream oss; 00750 oss << pint->getMin(); 00751 oss << ".."; 00752 oss << pint->getMax(); 00753 propvalues += oss.str(); 00754 } 00755 } 00756 for(Bitu k = 0; k < pv.size(); k++) { 00757 if (pv[k].ToString() =="%u") 00758 propvalues += MSG_Get("PROGRAM_CONFIG_HLP_POSINT"); 00759 else propvalues += pv[k].ToString(); 00760 if ((k+1) < pv.size()) propvalues += ", "; 00761 } 00762 00763 WriteOut(MSG_Get("PROGRAM_CONFIG_HLP_PROPHLP"), 00764 p->propname.c_str(), 00765 sec->GetName(), 00766 p->Get_help(),propvalues.c_str(), 00767 p->Get_Default_Value().ToString().c_str(), 00768 p->GetValue().ToString().c_str()); 00769 // print 'changability' 00770 if (p->getChange()==Property::Changeable::OnlyAtStart) { 00771 WriteOut(MSG_Get("PROGRAM_CONFIG_HLP_NOCHANGE")); 00772 } 00773 return; 00774 } 00775 } 00776 break; 00777 } 00778 return; 00779 } 00780 case P_AUTOEXEC_CLEAR: { 00781 Section_line* sec = dynamic_cast <Section_line*> 00782 (control->GetSection(std::string("autoexec"))); 00783 sec->data.clear(); 00784 break; 00785 } 00786 case P_AUTOEXEC_ADD: { 00787 if (pvars.size() == 0) { 00788 WriteOut(MSG_Get("PROGRAM_CONFIG_MISSINGPARAM")); 00789 return; 00790 } 00791 Section_line* sec = dynamic_cast <Section_line*> 00792 (control->GetSection(std::string("autoexec"))); 00793 00794 for(Bitu i = 0; i < pvars.size(); i++) sec->HandleInputline(pvars[i]); 00795 break; 00796 } 00797 case P_AUTOEXEC_TYPE: { 00798 Section_line* sec = dynamic_cast <Section_line*> 00799 (control->GetSection(std::string("autoexec"))); 00800 WriteOut("\n%s",sec->data.c_str()); 00801 break; 00802 } 00803 case P_GETPROP: { 00804 // "section property" 00805 // "property" 00806 // "section" 00807 // "section" "property" 00808 if (pvars.size()==0) { 00809 WriteOut(MSG_Get("PROGRAM_CONFIG_GET_SYNTAX")); 00810 return; 00811 } 00812 std::string::size_type spcpos = pvars[0].find_first_of(' '); 00813 // split on the ' ' 00814 if (spcpos != std::string::npos) { 00815 if (spcpos>1&&pvars[0].c_str()[spcpos-1]==',') 00816 spcpos=pvars[0].find_first_of(' ', spcpos+1); 00817 if (spcpos != std::string::npos) { 00818 pvars.insert(pvars.begin()+1,pvars[0].substr(spcpos+1)); 00819 pvars[0].erase(spcpos); 00820 } 00821 } 00822 switch(pvars.size()) { 00823 case 1: { 00824 // property/section only 00825 // is it a section? 00826 Section* sec = control->GetSection(pvars[0].c_str()); 00827 if (sec) { 00828 // list properties in section 00829 int i = 0; 00830 Section_prop* psec = dynamic_cast <Section_prop*>(sec); 00831 if (psec==NULL) { 00832 // autoexec section 00833 Section_line* pline = dynamic_cast <Section_line*>(sec); 00834 if (pline==NULL) E_Exit("Section dynamic cast failed."); 00835 00836 WriteOut("%s",pline->data.c_str()); 00837 break; 00838 } 00839 while(true) { 00840 // list the properties 00841 Property* p = psec->Get_prop(i++); 00842 if (p==NULL) break; 00843 WriteOut("%s=%s\n", p->propname.c_str(), 00844 p->GetValue().ToString().c_str()); 00845 } 00846 if (!strcasecmp(pvars[0].c_str(), "config")) { 00847 const char * extra = const_cast<char*>(psec->data.c_str()); 00848 if (extra&&strlen(extra)) { 00849 std::istringstream in(extra); 00850 char linestr[CROSS_LEN+1], cmdstr[CROSS_LEN], valstr[CROSS_LEN]; 00851 char *cmd=cmdstr, *val=valstr, *p; 00852 if (in) for (std::string line; std::getline(in, line); ) { 00853 if (line.length()>CROSS_LEN) { 00854 strncpy(linestr, line.c_str(), CROSS_LEN); 00855 linestr[CROSS_LEN]=0; 00856 } else 00857 strcpy(linestr, line.c_str()); 00858 p=strchr(linestr, '='); 00859 if (p!=NULL) { 00860 *p=0; 00861 strcpy(cmd, linestr); 00862 cmd=trim(cmd); 00863 strcpy(val, p+1); 00864 val=trim(val); 00865 lowcase(cmd); 00866 if (!strncmp(cmd, "set ", 4)||!strcmp(cmd, "install")||!strcmp(cmd, "installhigh")||!strcmp(cmd, "device")||!strcmp(cmd, "devicehigh")) 00867 WriteOut("%s=%s\n", cmd, val); 00868 } 00869 } 00870 } 00871 } 00872 } else { 00873 // no: maybe it's a property? 00874 sec = control->GetSectionFromProperty(pvars[0].c_str()); 00875 if (!sec) { 00876 WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR")); 00877 return; 00878 } 00879 // it's a property name 00880 std::string val = sec->GetPropValue(pvars[0].c_str()); 00881 WriteOut("%s",val.c_str()); 00882 first_shell->SetEnv("CONFIG",val.c_str()); 00883 } 00884 break; 00885 } 00886 case 2: { 00887 // section + property 00888 Section* sec = control->GetSection(pvars[0].c_str()); 00889 if (!sec) { 00890 WriteOut(MSG_Get("PROGRAM_CONFIG_SECTION_ERROR"), pvars[0].c_str()); 00891 return; 00892 } 00893 std::string val = sec->GetPropValue(pvars[1].c_str()); 00894 if (val == NO_SUCH_PROPERTY) { 00895 if (!strcasecmp(pvars[0].c_str(), "config") && (!strcasecmp(pvars[1].c_str(), "set") || !strcasecmp(pvars[1].c_str(), "device") || !strcasecmp(pvars[1].c_str(), "devicehigh") || !strcasecmp(pvars[1].c_str(), "install") || !strcasecmp(pvars[1].c_str(), "installhigh"))) { 00896 Section_prop* psec = dynamic_cast <Section_prop*>(sec); 00897 const char * extra = const_cast<char*>(psec->data.c_str()); 00898 if (extra&&strlen(extra)) { 00899 std::istringstream in(extra); 00900 char linestr[CROSS_LEN+1], cmdstr[CROSS_LEN], valstr[CROSS_LEN]; 00901 char *cmd=cmdstr, *val=valstr, *p; 00902 if (in) for (std::string line; std::getline(in, line); ) { 00903 if (line.length()>CROSS_LEN) { 00904 strncpy(linestr, line.c_str(), CROSS_LEN); 00905 linestr[CROSS_LEN]=0; 00906 } else 00907 strcpy(linestr, line.c_str()); 00908 p=strchr(linestr, '='); 00909 if (p!=NULL) { 00910 *p=0; 00911 strcpy(cmd, linestr); 00912 cmd=trim(cmd); 00913 strcpy(val, p+1); 00914 val=trim(val); 00915 lowcase(cmd); 00916 if (!strncasecmp(cmd, "set ", 4)&&!strcasecmp(pvars[1].c_str(), "set")) 00917 WriteOut("%s=%s\n", trim(cmd+4), val); 00918 else if(!strcasecmp(cmd, pvars[1].c_str())) 00919 WriteOut("%s\n", val); 00920 } 00921 } 00922 } 00923 } else 00924 WriteOut(MSG_Get("PROGRAM_CONFIG_NO_PROPERTY"), pvars[1].c_str(),pvars[0].c_str()); 00925 return; 00926 } 00927 WriteOut("%s",val.c_str()); 00928 first_shell->SetEnv("CONFIG",val.c_str()); 00929 break; 00930 } 00931 default: 00932 WriteOut(MSG_Get("PROGRAM_CONFIG_GET_SYNTAX")); 00933 return; 00934 } 00935 return; 00936 } 00937 case P_SETPROP: { 00938 // Code for the configuration changes 00939 // Official format: config -set "section property=value" 00940 // Accepted: with or without -set, 00941 // "section property=value" 00942 // "property" "value" 00943 // "section" "property=value" 00944 // "section" "property=value" "value" "value" ... 00945 // "section" "property" "value" 00946 // "section" "property" "value" "value" ... 00947 // "section property" "value" "value" ... 00948 // "property" "value" "value" ... 00949 // "property=value" "value" "value" ... 00950 00951 if (pvars.size()==0) { 00952 WriteOut(MSG_Get("PROGRAM_CONFIG_SET_SYNTAX")); 00953 return; 00954 } 00955 00956 // add rest of command 00957 std::string rest; 00958 if (cmd->GetStringRemain(rest)) pvars.push_back(rest); 00959 00960 // attempt to split off the first word 00961 std::string::size_type spcpos = pvars[0].find_first_of(' '); 00962 if (spcpos>1&&pvars[0].c_str()[spcpos-1]==',') 00963 spcpos=pvars[0].find_first_of(' ', spcpos+1); 00964 00965 std::string::size_type equpos = pvars[0].find_first_of('='); 00966 00967 if ((equpos != std::string::npos) && 00968 ((spcpos == std::string::npos) || (equpos < spcpos))) { 00969 // If we have a '=' possibly before a ' ' split on the = 00970 pvars.insert(pvars.begin()+1,pvars[0].substr(equpos+1)); 00971 pvars[0].erase(equpos); 00972 // As we had a = the first thing must be a property now 00973 Section* sec=control->GetSectionFromProperty(pvars[0].c_str()); 00974 if (sec) pvars.insert(pvars.begin(),std::string(sec->GetName())); 00975 else { 00976 WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR")); 00977 return; 00978 } 00979 // order in the vector should be ok now 00980 } else { 00981 if (equpos != std::string::npos && spcpos < equpos) { 00982 // ' ' before a possible '=', split on the ' ' 00983 pvars.insert(pvars.begin()+1,pvars[0].substr(spcpos+1)); 00984 pvars[0].erase(spcpos); 00985 } 00986 // check if the first parameter is a section or property 00987 Section* sec = control->GetSection(pvars[0].c_str()); 00988 if (!sec) { 00989 // not a section: little duplicate from above 00990 sec=control->GetSectionFromProperty(pvars[0].c_str()); 00991 if (sec) pvars.insert(pvars.begin(),std::string(sec->GetName())); 00992 else { 00993 WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR")); 00994 return; 00995 } 00996 } else { 00997 // first of pvars is most likely a section, but could still be gus 00998 // have a look at the second parameter 00999 if (pvars.size() < 2) { 01000 WriteOut(MSG_Get("PROGRAM_CONFIG_SET_SYNTAX")); 01001 return; 01002 } 01003 std::string::size_type equpos2 = pvars[1].find_first_of('='); 01004 if (equpos2 != std::string::npos) { 01005 // split on the = 01006 pvars.insert(pvars.begin()+2,pvars[1].substr(equpos2+1)); 01007 pvars[1].erase(equpos2); 01008 } 01009 // is this a property? 01010 Section* sec2 = control->GetSectionFromProperty(pvars[1].c_str()); 01011 if (!sec2) { 01012 // not a property, 01013 Section* sec3 = control->GetSectionFromProperty(pvars[0].c_str()); 01014 if (sec3) { 01015 // section and property name are identical 01016 pvars.insert(pvars.begin(),pvars[0]); 01017 } // else has been checked above already 01018 } 01019 } 01020 } 01021 if(pvars.size() < 3) { 01022 WriteOut(MSG_Get("PROGRAM_CONFIG_SET_SYNTAX")); 01023 return; 01024 } 01025 // check if the property actually exists in the section 01026 Section* sec2 = control->GetSectionFromProperty(pvars[1].c_str()); 01027 if (!sec2) { 01028 if (!strcasecmp(pvars[0].c_str(), "config") && (!strcasecmp(pvars[1].c_str(), "set") || !strcasecmp(pvars[1].c_str(), "device") || !strcasecmp(pvars[1].c_str(), "devicehigh") || !strcasecmp(pvars[1].c_str(), "install") || !strcasecmp(pvars[1].c_str(), "installhigh"))) 01029 WriteOut("Cannot set property %s in section %s.\n", pvars[1].c_str(),pvars[0].c_str()); 01030 else 01031 WriteOut(MSG_Get("PROGRAM_CONFIG_NO_PROPERTY"), pvars[1].c_str(),pvars[0].c_str()); 01032 return; 01033 } 01034 // Input has been parsed (pvar[0]=section, [1]=property, [2]=value) 01035 // now execute 01036 Section* tsec = control->GetSection(pvars[0]); 01037 std::string value(pvars[2]); 01038 //Due to parsing there can be a = at the start of value. 01039 while (value.size() && (value.at(0) ==' ' ||value.at(0) =='=') ) value.erase(0,1); 01040 for(Bitu i = 3; i < pvars.size(); i++) value += (std::string(" ") + pvars[i]); 01041 std::string inputline = pvars[1] + "=" + value; 01042 01043 bool change_success = tsec->HandleInputline(inputline.c_str()); 01044 if (change_success) { 01045 if (!strcasecmp(pvars[0].c_str(), "dosbox")||!strcasecmp(pvars[0].c_str(), "sdl")||!strcasecmp(pvars[0].c_str(), "dos")) { 01046 Section_prop *section = static_cast<Section_prop *>(control->GetSection(pvars[0].c_str())); 01047 if (section != NULL) { 01048 if (!strcasecmp(pvars[0].c_str(), "dosbox")) { 01049 force_nocachedir = section->Get_bool("nocachedir"); 01050 freesizecap = section->Get_bool("freesizecap"); 01051 wpcolon = section->Get_bool("leading colon write protect image"); 01052 } else if (!strcasecmp(pvars[0].c_str(), "sdl")) { 01053 modifier = section->Get_string("clip_key_modifier"); 01054 paste_speed = section->Get_int("clip_paste_speed"); 01055 wheel_key = section->Get_int("mouse_wheel_key"); 01056 #if defined(C_SDL2) 01057 if (!strcasecmp(inputline.substr(0, 16).c_str(), "mapperfile_sdl2=")) ReloadMapper(section,true); 01058 #else 01059 if (!strcasecmp(inputline.substr(0, 11).c_str(), "mapperfile=")) ReloadMapper(section,true); 01060 #endif 01061 } else if (!strcasecmp(pvars[0].c_str(), "dos")) { 01062 if (!strcasecmp(inputline.substr(0, 4).c_str(), "lfn=")) { 01063 if (!strcmp(section->Get_string("lfn"), "true")) enablelfn=1; 01064 else if (!strcmp(section->Get_string("lfn"), "false")) enablelfn=0; 01065 else if (!strcmp(section->Get_string("lfn"), "autostart")) enablelfn=-2; 01066 else enablelfn=-1; 01067 mainMenu.get_item("dos_lfn_auto").check(enablelfn==-1).refresh_item(mainMenu); 01068 mainMenu.get_item("dos_lfn_enable").check(enablelfn==1).refresh_item(mainMenu); 01069 mainMenu.get_item("dos_lfn_disable").check(enablelfn==0).refresh_item(mainMenu); 01070 uselfn = enablelfn==1 || ((enablelfn == -1 || enablelfn == -2) && dos.version.major>6); 01071 } else if (!strcasecmp(inputline.substr(0, 4).c_str(), "ver=")) { 01072 std::string ver = section->Get_string("ver"); 01073 if (!ver.empty()) { 01074 const char *s = ver.c_str(); 01075 if (isdigit(*s)) { 01076 dos.version.minor=0; 01077 dos.version.major=(int)strtoul(s,(char**)(&s),10); 01078 if (*s == '.' || *s == ' ') { 01079 s++; 01080 if (isdigit(*s)) 01081 dos.version.minor=(*(s-1)=='.'&&strlen(s)==1?10:1)*(int)strtoul(s,(char**)(&s),10); 01082 } 01083 uselfn = enablelfn==1 || ((enablelfn == -1 || enablelfn == -2) && dos.version.major>6); 01084 } 01085 } 01086 } 01087 } 01088 } 01089 } 01090 } else WriteOut(MSG_Get("PROGRAM_CONFIG_VALUE_ERROR"), 01091 value.c_str(),pvars[1].c_str()); 01092 return; 01093 } 01094 case P_WRITELANG: case P_WRITELANG2: 01095 // In secure mode don't allow a new languagefile to be created 01096 // Who knows which kind of file we would overwrite. 01097 if (securemode_check()) return; 01098 if (pvars.size() < 1) { 01099 WriteOut(MSG_Get("PROGRAM_CONFIG_MISSINGPARAM")); 01100 return; 01101 } 01102 if (!MSG_Write(pvars[0].c_str())) { 01103 WriteOut(MSG_Get("PROGRAM_CONFIG_FILE_ERROR"),pvars[0].c_str()); 01104 return; 01105 } 01106 break; 01107 01108 case P_SECURE: 01109 // Code for switching to secure mode 01110 control->SwitchToSecureMode(); 01111 WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_ON")); 01112 return; 01113 01114 default: 01115 E_Exit("bug"); 01116 break; 01117 } 01118 first = false; 01119 } 01120 return; 01121 } 01122 01123 01124 static void CONFIG_ProgramStart(Program * * make) { 01125 *make=new CONFIG; 01126 } 01127 01128 void PROGRAMS_DOS_Boot(Section *) { 01129 PROGRAMS_MakeFile("CONFIG.COM",CONFIG_ProgramStart); 01130 } 01131 01132 /* FIXME: Rename the function to clarify it does not init programs, it inits the callback mechanism 01133 * that program generation on drive Z: needs to tie a .COM executable to a callback */ 01134 void PROGRAMS_Init() { 01135 LOG(LOG_MISC,LOG_DEBUG)("PROGRAMS_Init(): initializing Z: drive .COM stub and program management"); 01136 01137 /* Setup a special callback to start virtual programs */ 01138 call_program=CALLBACK_Allocate(); 01139 CALLBACK_Setup(call_program,&PROGRAMS_Handler,CB_RETF,"internal program"); 01140 01141 AddVMEventFunction(VM_EVENT_DOS_INIT_KERNEL_READY,AddVMEventFunctionFuncPair(PROGRAMS_DOS_Boot)); 01142 01143 // listconf 01144 MSG_Add("PROGRAM_CONFIG_NOCONFIGFILE","No config file loaded!\n"); 01145 MSG_Add("PROGRAM_CONFIG_PRIMARY_CONF","Primary config file: \n%s\n"); 01146 MSG_Add("PROGRAM_CONFIG_ADDITIONAL_CONF","Additional config files:\n"); 01147 MSG_Add("PROGRAM_CONFIG_CONFDIR","DOSBox-X %s configuration directory: \n%s\n\n"); 01148 01149 // writeconf 01150 MSG_Add("PROGRAM_CONFIG_FILE_ERROR","\nCan't open file %s\n"); 01151 MSG_Add("PROGRAM_CONFIG_FILE_WHICH","Writing config file %s\n"); 01152 01153 // help 01154 MSG_Add("PROGRAM_CONFIG_USAGE","The DOSBox-X configuration tool. Supported options:\n\n"\ 01155 "-wc (or -writeconf) without parameter: Writes to primary loaded config file.\n"\ 01156 "-wc (or -writeconf) with filename: Writes file to the config directory.\n"\ 01157 "-wl (or -writelang) with filename: Writes the current language strings.\n"\ 01158 "-wcp [filename] Writes config file to the program directory (dosbox-x.conf\n or the specified filename).\n"\ 01159 "-wcd Writes to the default config file in the config directory.\n"\ 01160 "-all Use this with -wc, -wcp, or -wcd to write ALL options to the config file.\n"\ 01161 "-l Lists DOSBox-X configuration parameters.\n"\ 01162 "-h, -help, -? sections / sectionname / propertyname\n"\ 01163 " Without parameters, displays this help screen. Add \"sections\" for a list of\n sections."\ 01164 " For info about a specific section or property add its name behind.\n"\ 01165 "-axclear Clears the [autoexec] section.\n"\ 01166 "-axadd [line] Adds a line to the [autoexec] section.\n"\ 01167 "-axtype Prints the content of the [autoexec] section.\n"\ 01168 "-securemode Switches to secure mode where MOUNT, IMGMOUNT and BOOT will be\n"\ 01169 " disabled as well as the ability to create config and language files.\n"\ 01170 "-get \"section property\" returns the value of the property.\n"\ 01171 "-set \"section property=value\" sets the value of the property.\n" ); 01172 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"); 01173 MSG_Add("PROGRAM_CONFIG_HLP_LINEHLP","Purpose of section \"%s\":\n%s\nCurrent value:\n%s\n"); 01174 MSG_Add("PROGRAM_CONFIG_HLP_NOCHANGE","This property cannot be changed at runtime.\n"); 01175 MSG_Add("PROGRAM_CONFIG_HLP_POSINT","positive integer"); 01176 MSG_Add("PROGRAM_CONFIG_HLP_SECTHLP","Section %s contains the following properties:\n"); 01177 MSG_Add("PROGRAM_CONFIG_HLP_SECTLIST","DOSBox-X configuration contains the following sections:\n\n"); 01178 01179 MSG_Add("PROGRAM_CONFIG_SECURE_ON","Switched to secure mode.\n"); 01180 MSG_Add("PROGRAM_CONFIG_SECURE_DISALLOW","This operation is not permitted in secure mode.\n"); 01181 MSG_Add("PROGRAM_CONFIG_SECTION_ERROR","Section %s doesn't exist.\n"); 01182 MSG_Add("PROGRAM_CONFIG_VALUE_ERROR","\"%s\" is not a valid value for property %s.\n"); 01183 MSG_Add("PROGRAM_CONFIG_PROPERTY_ERROR","No such section or property.\n"); 01184 MSG_Add("PROGRAM_CONFIG_PROPERTY_DUPLICATE","There may be other sections with the same property name.\n"); 01185 MSG_Add("PROGRAM_CONFIG_NO_PROPERTY","There is no property %s in section %s.\n"); 01186 MSG_Add("PROGRAM_CONFIG_SET_SYNTAX","Correct syntax: config -set \"section property=value\".\n"); 01187 MSG_Add("PROGRAM_CONFIG_GET_SYNTAX","Correct syntax: config -get \"section property\".\n"); 01188 MSG_Add("PROGRAM_CONFIG_PRINT_STARTUP","\nDOSBox-X was started with the following command line parameters:\n%s"); 01189 MSG_Add("PROGRAM_CONFIG_MISSINGPARAM","Missing parameter."); 01190 }