DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/misc/programs.cpp
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 }