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