DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
src/shell/shell.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  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 #include <assert.h>
00020 #include <stdlib.h>
00021 #include <stdarg.h>
00022 #include <string.h>
00023 #include "dosbox.h"
00024 #include "regs.h"
00025 #include "control.h"
00026 #include "shell.h"
00027 #include "menu.h"
00028 #include "callback.h"
00029 #include "support.h"
00030 #include "builtin.h"
00031 #include "build_timestamp.h"
00032 
00033 extern bool dos_shell_running_program;
00034 
00035 void CALLBACK_DeAllocate(Bitu in);
00036 
00037 Bitu call_shellstop = 0;
00038 /* Larger scope so shell_del autoexec can use it to
00039  * remove things from the environment */
00040 Program * first_shell = 0; 
00041 
00042 static Bitu shellstop_handler(void) {
00043         return CBRET_STOP;
00044 }
00045 
00046 static void SHELL_ProgramStart(Program * * make) {
00047         *make = new DOS_Shell;
00048 }
00049 
00050 #define AUTOEXEC_SIZE 4096
00051 static char autoexec_data[AUTOEXEC_SIZE] = { 0 };
00052 static std::list<std::string> autoexec_strings;
00053 typedef std::list<std::string>::iterator auto_it;
00054 
00055 void VFILE_Remove(const char *name);
00056 
00057 void AutoexecObject::Install(const std::string &in) {
00058         if(GCC_UNLIKELY(installed)) E_Exit("autoexec: already created %s",buf.c_str());
00059         installed = true;
00060         buf = in;
00061         autoexec_strings.push_back(buf);
00062         this->CreateAutoexec();
00063 
00064         //autoexec.bat is normally created AUTOEXEC_Init.
00065         //But if we are already running (first_shell)
00066         //we have to update the envirionment to display changes
00067 
00068         if(first_shell) {
00069                 //create a copy as the string will be modified
00070                 std::string::size_type n = buf.size();
00071                 char* buf2 = new char[n + 1];
00072                 safe_strncpy(buf2, buf.c_str(), n + 1);
00073                 if((strncasecmp(buf2,"set ",4) == 0) && (strlen(buf2) > 4)){
00074                         char* after_set = buf2 + 4;//move to variable that is being set
00075                         char* test = strpbrk(after_set,"=");
00076                         if(!test) {first_shell->SetEnv(after_set,"");return;}
00077                         *test++ = 0;
00078                         //If the shell is running/exists update the environment
00079                         first_shell->SetEnv(after_set,test);
00080                 }
00081                 delete [] buf2;
00082         }
00083 }
00084 
00085 void AutoexecObject::InstallBefore(const std::string &in) {
00086         if(GCC_UNLIKELY(installed)) E_Exit("autoexec: already created %s",buf.c_str());
00087         installed = true;
00088         buf = in;
00089         autoexec_strings.push_front(buf);
00090         this->CreateAutoexec();
00091 }
00092 
00093 void AutoexecObject::CreateAutoexec(void) {
00094         /* Remove old autoexec.bat if the shell exists */
00095         if(first_shell) VFILE_Remove("AUTOEXEC.BAT");
00096 
00097         //Create a new autoexec.bat
00098         autoexec_data[0] = 0;
00099         size_t auto_len;
00100         for(auto_it it=  autoexec_strings.begin(); it != autoexec_strings.end(); it++) {
00101                 auto_len = strlen(autoexec_data);
00102                 if ((auto_len+(*it).length()+3)>AUTOEXEC_SIZE) {
00103                         E_Exit("SYSTEM:Autoexec.bat file overflow");
00104                 }
00105                 sprintf((autoexec_data+auto_len),"%s\r\n",(*it).c_str());
00106         }
00107         if(first_shell) VFILE_Register("AUTOEXEC.BAT",(Bit8u *)autoexec_data,(Bit32u)strlen(autoexec_data));
00108 }
00109 
00110 void AutoexecObject::Uninstall() {
00111         if(!installed) return;
00112 
00113         // Remove the line from the autoexecbuffer and update environment
00114         for(auto_it it = autoexec_strings.begin(); it != autoexec_strings.end(); ) {
00115                 if((*it) == buf) {
00116                         it = autoexec_strings.erase(it);
00117                         std::string::size_type n = buf.size();
00118                         char* buf2 = new char[n + 1];
00119                         safe_strncpy(buf2, buf.c_str(), n + 1);
00120                         // If it's a environment variable remove it from there as well
00121                         if((strncasecmp(buf2,"set ",4) == 0) && (strlen(buf2) > 4)){
00122                                 char* after_set = buf2 + 4;//move to variable that is being set
00123                                 char* test = strpbrk(after_set,"=");
00124                                 if(!test) continue;
00125                                 *test = 0;
00126                                 //If the shell is running/exists update the environment
00127                                 if(first_shell) first_shell->SetEnv(after_set,"");
00128                         }
00129                         delete [] buf2;
00130                 } else it++;
00131         }
00132         installed=false;
00133         this->CreateAutoexec();
00134 }
00135 
00136 AutoexecObject::~AutoexecObject(){
00137         Uninstall();
00138 }
00139 
00140 DOS_Shell::~DOS_Shell() {
00141         if (bf != NULL) delete bf; /* free batch file */
00142 }
00143 
00144 DOS_Shell::DOS_Shell():Program(){
00145         input_handle=STDIN;
00146         echo=true;
00147         exit=false;
00148         bf=0;
00149         call=false;
00150     input_eof=false;
00151 }
00152 
00153 Bitu DOS_Shell::GetRedirection(char *s, char **ifn, char **ofn,bool * append) {
00154 
00155         char * lr=s;
00156         char * lw=s;
00157         char ch;
00158         Bitu num=0;
00159         bool quote = false;
00160         char* t;
00161 
00162         while ( (ch=*lr++) ) {
00163                 if(quote && ch != '"') { /* don't parse redirection within quotes. Not perfect yet. Escaped quotes will mess the count up */
00164                         *lw++ = ch;
00165                         continue;
00166                 }
00167 
00168                 switch (ch) {
00169                 case '"':
00170                         quote = !quote;
00171                         break;
00172                 case '>':
00173                         *append=((*lr)=='>');
00174                         if (*append) lr++;
00175                         lr=ltrim(lr);
00176                         if (*ofn) free(*ofn);
00177                         *ofn=lr;
00178                         while (*lr && *lr!=' ' && *lr!='<' && *lr!='|') lr++;
00179                         //if it ends on a : => remove it.
00180                         if((*ofn != lr) && (lr[-1] == ':')) lr[-1] = 0;
00181 //                      if(*lr && *(lr+1)) 
00182 //                              *lr++=0; 
00183 //                      else 
00184 //                              *lr=0;
00185                         t = (char*)malloc(lr-*ofn+1); // FIXME: *ofn is signed char, so if extended ASCII, could cause an error here!
00186                         safe_strncpy(t,*ofn,lr-*ofn+1); // FIXME: *ofn is signed char, so if extended ASCII, could cause an error here!
00187                         *ofn=t;
00188                         continue;
00189                 case '<':
00190                         if (*ifn) free(*ifn);
00191                         lr=ltrim(lr);
00192                         *ifn=lr;
00193                         while (*lr && *lr!=' ' && *lr!='>' && *lr != '|') lr++;
00194                         if((*ifn != lr) && (lr[-1] == ':')) lr[-1] = 0;
00195 //                      if(*lr && *(lr+1)) 
00196 //                              *lr++=0; 
00197 //                      else 
00198 //                              *lr=0;
00199                         t = (char*)malloc(lr-*ifn+1); // FIXME: *ofn is signed char, so if extended ASCII, could cause an error here!
00200                         safe_strncpy(t,*ifn,lr-*ifn+1); // FIXME: *ofn is signed char, so if extended ASCII, could cause an error here!
00201                         *ifn=t;
00202                         continue;
00203                 case '|':
00204                         ch=0;
00205                         num++;
00206                 }
00207                 *lw++=ch;
00208         }
00209         *lw=0;
00210         return num;
00211 }       
00212 
00213 void DOS_Shell::ParseLine(char * line) {
00214         LOG(LOG_EXEC,LOG_DEBUG)("Parsing command line: %s",line);
00215         /* Check for a leading @ */
00216         if (line[0] == '@') line[0] = ' ';
00217         line = trim(line);
00218 
00219         /* Do redirection and pipe checks */
00220         
00221         char * in  = 0;
00222         char * out = 0;
00223 
00224         Bit16u dummy,dummy2;
00225         Bit32u bigdummy = 0;
00226         Bitu num = 0;           /* Number of commands in this line */
00227         bool append;
00228         bool normalstdin  = false;      /* wether stdin/out are open on start. */
00229         bool normalstdout = false;      /* Bug: Assumed is they are "con"      */
00230         
00231         num = GetRedirection(line,&in, &out,&append);
00232         if (num>1) LOG_MSG("SHELL:Multiple command on 1 line not supported");
00233         if (in || out) {
00234                 normalstdin  = (psp->GetFileHandle(0) != 0xff); 
00235                 normalstdout = (psp->GetFileHandle(1) != 0xff); 
00236         }
00237         if (in) {
00238                 if(DOS_OpenFile(in,OPEN_READ,&dummy)) { //Test if file exists
00239                         DOS_CloseFile(dummy);
00240                         LOG_MSG("SHELL:Redirect input from %s",in);
00241                         if(normalstdin) DOS_CloseFile(0);       //Close stdin
00242                         DOS_OpenFile(in,OPEN_READ,&dummy);      //Open new stdin
00243                 }
00244         }
00245         if (out){
00246                 LOG_MSG("SHELL:Redirect output to %s",out);
00247                 if(normalstdout) DOS_CloseFile(1);
00248                 if(!normalstdin && !in) DOS_OpenFile("con",OPEN_READWRITE,&dummy);
00249                 bool status = true;
00250                 /* Create if not exist. Open if exist. Both in read/write mode */
00251                 if(append) {
00252                         if( (status = DOS_OpenFile(out,OPEN_READWRITE,&dummy)) ) {
00253                                  DOS_SeekFile(1,&bigdummy,DOS_SEEK_END);
00254                         } else {
00255                                 status = DOS_CreateFile(out,DOS_ATTR_ARCHIVE,&dummy);   //Create if not exists.
00256                         }
00257                 } else {
00258                         status = DOS_OpenFileExtended(out,OPEN_READWRITE,DOS_ATTR_ARCHIVE,0x12,&dummy,&dummy2);
00259                 }
00260                 
00261                 if(!status && normalstdout) DOS_OpenFile("con",OPEN_READWRITE,&dummy); //Read only file, open con again
00262                 if(!normalstdin && !in) DOS_CloseFile(0);
00263         }
00264         /* Run the actual command */
00265 
00266         if (this == first_shell) dos_shell_running_program = true;
00267 #if defined(WIN32) && !defined(C_SDL2)
00268         int Reflect_Menu(void);
00269         Reflect_Menu();
00270 #endif
00271 
00272         DoCommand(line);
00273 
00274         if (this == first_shell) dos_shell_running_program = false;
00275 #if defined(WIN32) && !defined(C_SDL2)
00276         int Reflect_Menu(void);
00277         Reflect_Menu();
00278 #endif
00279 
00280         /* Restore handles */
00281         if(in) {
00282                 DOS_CloseFile(0);
00283                 if(normalstdin) DOS_OpenFile("con",OPEN_READWRITE,&dummy);
00284                 free(in);
00285         }
00286         if(out) {
00287                 DOS_CloseFile(1);
00288                 if(!normalstdin) DOS_OpenFile("con",OPEN_READWRITE,&dummy);
00289                 if(normalstdout) DOS_OpenFile("con",OPEN_READWRITE,&dummy);
00290                 if(!normalstdin) DOS_CloseFile(0);
00291                 free(out);
00292         }
00293 }
00294 
00295 
00296 
00297 void DOS_Shell::RunInternal(void) {
00298         char input_line[CMD_MAXLINE] = {0};
00299         while (bf) {
00300                 if (bf->ReadLine(input_line)) {
00301                         if (echo) {
00302                                 if (input_line[0] != '@') {
00303                                         ShowPrompt();
00304                                         WriteOut_NoParsing(input_line);
00305                                         WriteOut_NoParsing("\n");
00306                                 }
00307                         }
00308                         ParseLine(input_line);
00309                         if (echo) WriteOut_NoParsing("\n");
00310                 }
00311         }
00312 }
00313 
00314 void DOS_Shell::Run(void) {
00315         char input_line[CMD_MAXLINE] = {0};
00316         std::string line;
00317         if (cmd->FindStringRemainBegin("/C",line)) {
00318                 strcpy(input_line,line.c_str());
00319                 char* sep = strpbrk(input_line,"\r\n"); //GTA installer
00320                 if (sep) *sep = 0;
00321                 DOS_Shell temp;
00322                 temp.echo = echo;
00323                 temp.ParseLine(input_line);             //for *.exe *.com  |*.bat creates the bf needed by runinternal;
00324                 temp.RunInternal();                             // exits when no bf is found.
00325                 return;
00326         }
00327 
00328     if (this == first_shell) {
00329         /* Start a normal shell and check for a first command init */
00330         WriteOut(MSG_Get("SHELL_STARTUP_BEGIN"),VERSION,UPDATED_STR);
00331 #if C_DEBUG
00332         WriteOut(MSG_Get("SHELL_STARTUP_DEBUG"));
00333 #endif
00334         if (machine == MCH_CGA || machine == MCH_AMSTRAD) WriteOut(MSG_Get("SHELL_STARTUP_CGA"));
00335         if (machine == MCH_PC98) WriteOut(MSG_Get("SHELL_STARTUP_PC98"));
00336         if (machine == MCH_HERC) WriteOut(MSG_Get("SHELL_STARTUP_HERC"));
00337         WriteOut(MSG_Get("SHELL_STARTUP_END"));
00338     }
00339     else {
00340         WriteOut("DOSBox command shell %s %s\n\n",VERSION,UPDATED_STR);
00341     }
00342 
00343         if (cmd->FindString("/INIT",line,true)) {
00344                 strcpy(input_line,line.c_str());
00345                 line.erase();
00346                 ParseLine(input_line);
00347         }
00348         do {
00349                 /* Get command once a line */
00350                 if (bf) {
00351                         if (bf->ReadLine(input_line)) {
00352                                 if (echo) {
00353                                         if (input_line[0]!='@') {
00354                                                 ShowPrompt();
00355                                                 WriteOut_NoParsing(input_line);
00356                                                 WriteOut_NoParsing("\n");
00357                                         };
00358                                 };
00359                         } else input_line[0]='\0';
00360                 } else {
00361                         if (echo) ShowPrompt();
00362                         InputCommand(input_line);
00363                         if (echo && !input_eof) WriteOut("\n");
00364 
00365             /* Bugfix: CTTY NUL will return immediately, the shell input will return
00366              *         immediately, and if we don't consume CPU cycles to compensate,
00367              *         will leave DOSBox-X running in an endless loop, hung. */
00368             if (input_eof) CALLBACK_Idle();
00369                 }
00370 
00371                 /* do it */
00372                 if(strlen(input_line)!=0) {
00373                         ParseLine(input_line);
00374                         if (echo && !bf) WriteOut_NoParsing("\n");
00375                 }
00376         } while (!exit);
00377 }
00378 
00379 void DOS_Shell::SyntaxError(void) {
00380         WriteOut(MSG_Get("SHELL_SYNTAXERROR"));
00381 }
00382 
00383 class AUTOEXEC:public Module_base {
00384 private:
00385         AutoexecObject autoexec[17];
00386         AutoexecObject autoexec_echo;
00387     AutoexecObject autoexec_auto_bat;
00388 public:
00389         AUTOEXEC(Section* configuration):Module_base(configuration) {
00390                 /* Register a virtual AUOEXEC.BAT file */
00391                 std::string line;
00392                 Section_line * section=static_cast<Section_line *>(configuration);
00393 
00394                 /* Check -securemode switch to disable mount/imgmount/boot after running autoexec.bat */
00395                 bool secure = control->opt_securemode;
00396 
00397         /* The user may have given .BAT files to run on the command line */
00398         if (!control->auto_bat_additional.empty()) {
00399             std::string cmd;
00400 
00401             for (unsigned int i=0;i<control->auto_bat_additional.size();i++) {
00402                                 std::string batname;
00403                                 /* NTS: this code might have problems with DBCS filenames - yksoft1 */
00404                                 size_t pos = control->auto_bat_additional[i].find_last_of(CROSS_FILESPLIT);
00405                                 if(pos == std::string::npos) {  //Only a filename, mount current directory
00406                                         batname = control->auto_bat_additional[i];
00407                                         cmd += "@mount c: . -q\n";
00408                                 } else { //Parse the path of .BAT file
00409                                         std::string batpath = control->auto_bat_additional[i].substr(0,pos+1);
00410                                         batname = control->auto_bat_additional[i].substr(pos+1);
00411                                         cmd += "@mount c: " + batpath + " -q\n";
00412                                 }
00413                                 cmd += "@c:\n";
00414                                 cmd += "@cd \\\n";
00415                 /* NTS: "CALL" does not support quoting the filename.
00416                  *      This will break if the batch file name has spaces in it. */
00417                 cmd += "@CALL ";
00418                 cmd += batname;
00419                 cmd += "\n";
00420                                 cmd += "@mount -u c: -q\n";
00421             }
00422 
00423             autoexec_auto_bat.Install(cmd);
00424         }
00425 
00426                 /* add stuff from the configfile unless -noautexec or -securemode is specified. */
00427                 char * extra = const_cast<char*>(section->data.c_str());
00428                 if (extra && !secure && !control->opt_noautoexec) {
00429                         /* detect if "echo off" is the first line */
00430                         bool echo_off  = !strncasecmp(extra,"echo off",8);
00431                         if (!echo_off) echo_off = !strncasecmp(extra,"@echo off",9);
00432 
00433                         /* if "echo off" add it to the front of autoexec.bat */
00434                         if(echo_off) autoexec_echo.InstallBefore("@echo off");
00435 
00436                         /* Install the stuff from the configfile */
00437                         autoexec[0].Install(section->data);
00438                 }
00439 
00440                 /* Check to see for extra command line options to be added (before the command specified on commandline) */
00441                 /* Maximum of extra commands: 10 */
00442                 Bitu i = 1;
00443                 for (auto it=control->opt_c.begin();i <= 11 && it!=control->opt_c.end();it++) /* -c switches */
00444                         autoexec[i++].Install(*it);
00445 
00446                 /* Check for the -exit switch which causes dosbox to when the command on the commandline has finished */
00447                 bool addexit = control->opt_exit;
00448 
00449 #if 0/*FIXME: This is ugly. I don't care to follow through on this nonsense for now. When needed, port to new command line switching. */
00450                 /* Check for first command being a directory or file */
00451                 char buffer[CROSS_LEN];
00452                 char orig[CROSS_LEN];
00453                 char cross_filesplit[2] = {CROSS_FILESPLIT , 0};
00454                 /* Combining -securemode and no parameter leaves you with a lovely Z:\. */ 
00455                 if ( !control->cmdline->FindCommand(1,line) ) { 
00456                         if ( secure ) autoexec[12].Install("z:\\config.com -securemode");
00457                 } else {
00458                         if (line.find(':',((line[0]|0x20) >= 'a' && (line[0]|0x20) <= 'z')?2:0) != std::string::npos) {
00459                                 /* a physfs source */
00460                                 autoexec[12].Install(std::string("MOUNT C \"") + line + std::string("\""));
00461                                 autoexec[13].Install("C:");
00462                                 if(secure) autoexec[14].Install("z:\\config.com -securemode");
00463                                 goto nomount;
00464                         }
00465 
00466                         struct stat test;
00467                         strcpy(buffer,line.c_str());
00468                         if (stat(buffer,&test)){
00469                                 getcwd(buffer,CROSS_LEN);
00470                                 strcat(buffer,cross_filesplit);
00471                                 strcat(buffer,line.c_str());
00472                                 if (stat(buffer,&test)) goto nomount;
00473                         }
00474                         if (test.st_mode & S_IFDIR) { 
00475                                 autoexec[12].Install(std::string("MOUNT C \"") + buffer + "\"");
00476                                 autoexec[13].Install("C:");
00477                                 if(secure) autoexec[14].Install("z:\\config.com -securemode");
00478                         } else {
00479                                 char* name = strrchr(buffer,CROSS_FILESPLIT);
00480                                 if (!name) { //Only a filename 
00481                                         line = buffer;
00482                                         getcwd(buffer,CROSS_LEN);
00483                                         strcat(buffer,cross_filesplit);
00484                                         strcat(buffer,line.c_str());
00485                                         if(stat(buffer,&test)) goto nomount;
00486                                         name = strrchr(buffer,CROSS_FILESPLIT);
00487                                         if(!name) goto nomount;
00488                                 }
00489                                 *name++ = 0;
00490                                 if (access(buffer,F_OK)) goto nomount;
00491                                 upcase(name);
00492                                 autoexec[12].Install(std::string("MOUNT C \"") + buffer + "\"");
00493                                 autoexec[13].Install("C:");
00494                                 /* Save the non-modified filename (so boot and imgmount can use it (long filenames, case sensivitive)) */
00495                                 strcpy(orig,name);
00496                                 if(strstr(name,".BAT") != 0) {
00497                                         if(secure) autoexec[14].Install("z:\\config.com -securemode");
00498                                         /* BATch files are called else exit will not work */
00499                                         autoexec[15].Install(std::string("CALL ") + name);
00500                                         if(addexit) autoexec[16].Install("exit");
00501                                 } else if((strstr(name,".IMG") != 0) || (strstr(name,".IMA") !=0 )) {
00502                                         //No secure mode here as boot is destructive and enabling securemode disables boot
00503                                         /* Boot image files */
00504                                         autoexec[15].Install(std::string("BOOT ") + orig);
00505                                 } else if((strstr(name,".ISO") != 0) || (strstr(name,".CUE") !=0 )) {
00506                                         /* imgmount CD image files */
00507                                         /* securemode gets a different number from the previous branches! */
00508                                         autoexec[14].Install(std::string("IMGMOUNT D \"") + orig + std::string("\" -t iso"));
00509                                         //autoexec[16].Install("D:");
00510                                         if(secure) autoexec[15].Install("z:\\config.com -securemode");
00511                                         /* Makes no sense to exit here */
00512                                 } else {
00513                                         if(secure) autoexec[14].Install("z:\\config.com -securemode");
00514                                         autoexec[15].Install(name);
00515                                         if(addexit) autoexec[16].Install("exit");
00516                                 }
00517                         }
00518                 }
00519 nomount:
00520 #endif
00521 
00522                 if (addexit) autoexec[i++].Install("exit");
00523 
00524                 assert(i <= 17); /* FIXME: autoexec[] should not be fixed size */
00525 
00526                 VFILE_Register("AUTOEXEC.BAT",(Bit8u *)autoexec_data,(Bit32u)strlen(autoexec_data));
00527         }
00528 };
00529 
00530 static AUTOEXEC* test = NULL;
00531         
00532 static void AUTOEXEC_ShutDown(Section * sec) {
00533     (void)sec;//UNUSED
00534         if (test != NULL) {
00535                 delete test;
00536                 test = NULL;
00537         }
00538     if (first_shell != NULL) {
00539                 delete first_shell;
00540                 first_shell = 0;//Make clear that it shouldn't be used anymore
00541     }
00542     if (call_shellstop != 0) {
00543         CALLBACK_DeAllocate(call_shellstop);
00544         call_shellstop = 0;
00545     }
00546 }
00547 
00548 void AUTOEXEC_Startup(Section *sec) {
00549     (void)sec;//UNUSED
00550         if (test == NULL) {
00551                 LOG(LOG_MISC,LOG_DEBUG)("Allocating AUTOEXEC.BAT emulation");
00552                 test = new AUTOEXEC(control->GetSection("autoexec"));
00553         }
00554 }
00555 
00556 void AUTOEXEC_Init() {
00557         LOG(LOG_MISC,LOG_DEBUG)("Initializing AUTOEXEC.BAT emulation");
00558 
00559         AddExitFunction(AddExitFunctionFuncPair(AUTOEXEC_ShutDown));
00560         AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(AUTOEXEC_ShutDown));
00561         AddVMEventFunction(VM_EVENT_DOS_EXIT_BEGIN,AddVMEventFunctionFuncPair(AUTOEXEC_ShutDown));
00562         AddVMEventFunction(VM_EVENT_DOS_EXIT_REBOOT_BEGIN,AddVMEventFunctionFuncPair(AUTOEXEC_ShutDown));
00563         AddVMEventFunction(VM_EVENT_DOS_SURPRISE_REBOOT,AddVMEventFunctionFuncPair(AUTOEXEC_ShutDown));
00564 }
00565 
00566 static char const * const path_string="PATH=Z:\\";
00567 static char const * const comspec_string="COMSPEC=Z:\\COMMAND.COM";
00568 static char const * const prompt_string="PROMPT=$P$G";
00569 static char const * const full_name="Z:\\COMMAND.COM";
00570 static char const * const init_line="/INIT AUTOEXEC.BAT";
00571 
00572 extern unsigned int dosbox_shell_env_size;
00573 
00574 /* TODO: Why is all this DOS kernel and VFILE registration here in SHELL_Init()?
00575  *       That's like claiming that DOS memory and device initialization happens from COMMAND.COM!
00576  *       We need to move the DOS kernel initialization into another function, and the VFILE
00577  *       registration to another function, and then message initialization to another function,
00578  *       and then those functions need to be called before SHELL_Init() -J.C. */
00579 void SHELL_Init() {
00580         LOG(LOG_MISC,LOG_DEBUG)("Initializing DOS shell");
00581 
00582         /* Add messages */
00583         MSG_Add("SHELL_CMD_VOL_DRIVE","\n Volume in drive %c ");
00584         MSG_Add("SHELL_CMD_VOL_DRIVEERROR","Cannot find the drive specified\n");
00585         MSG_Add("SHELL_CMD_VOL_SERIAL"," Volume Serial Number is ");
00586         MSG_Add("SHELL_CMD_VOL_SERIAL_NOLABEL","has no label\n");
00587         MSG_Add("SHELL_CMD_VOL_SERIAL_LABEL","is %s\n");
00588         MSG_Add("SHELL_ILLEGAL_PATH","Illegal Path.\n");
00589         MSG_Add("SHELL_CMD_HELP","If you want a list of all supported commands type \033[33;1mhelp /all\033[0m .\nA short list of the most often used commands:\n");
00590         MSG_Add("SHELL_CMD_ECHO_ON","ECHO is on.\n");
00591         MSG_Add("SHELL_CMD_ECHO_OFF","ECHO is off.\n");
00592         MSG_Add("SHELL_ILLEGAL_SWITCH","Illegal switch: %s.\n");
00593         MSG_Add("SHELL_MISSING_PARAMETER","Required parameter missing.\n");
00594         MSG_Add("SHELL_CMD_CHDIR_ERROR","Unable to change to: %s.\n");
00595         MSG_Add("SHELL_CMD_CHDIR_HINT","Hint: To change to different drive type \033[31m%c:\033[0m\n");
00596         MSG_Add("SHELL_CMD_CHDIR_HINT_2","directoryname is longer than 8 characters and/or contains spaces.\nTry \033[31mcd %s\033[0m\n");
00597         MSG_Add("SHELL_CMD_CHDIR_HINT_3","You are still on drive Z:, change to a mounted drive with \033[31mC:\033[0m.\n");
00598         MSG_Add("SHELL_CMD_DATE_HELP","Displays or changes the internal date.\n");
00599         MSG_Add("SHELL_CMD_DATE_ERROR","The specified date is not correct.\n");
00600         MSG_Add("SHELL_CMD_DATE_DAYS","3SunMonTueWedThuFriSat"); // "2SoMoDiMiDoFrSa"
00601         MSG_Add("SHELL_CMD_DATE_NOW","Current date: ");
00602         MSG_Add("SHELL_CMD_DATE_SETHLP","Type 'date MM-DD-YYYY' to change.\n");
00603         MSG_Add("SHELL_CMD_DATE_FORMAT","M/D/Y");
00604         MSG_Add("SHELL_CMD_DATE_HELP_LONG","DATE [[/T] [/H] [/S] | MM-DD-YYYY]\n"\
00605                                                                         "  MM-DD-YYYY: new date to set\n"\
00606                                                                         "  /S:         Permanently use host time and date as DOS time\n"\
00607                                     "  /F:         Switch back to DOSBox internal time (opposite of /S)\n"\
00608                                                                         "  /T:         Only display date\n"\
00609                                                                         "  /H:         Synchronize with host\n");
00610         MSG_Add("SHELL_CMD_TIME_HELP","Displays the internal time.\n");
00611         MSG_Add("SHELL_CMD_TIME_NOW","Current time: ");
00612         MSG_Add("SHELL_CMD_TIME_HELP_LONG","TIME [/T] [/H]\n"\
00613                                                                         "  /T:         Display simple time\n"\
00614                                                                         "  /H:         Synchronize with host\n");
00615         MSG_Add("SHELL_CMD_MKDIR_ERROR","Unable to make: %s.\n");
00616         MSG_Add("SHELL_CMD_RMDIR_ERROR","Unable to remove: %s.\n");
00617         MSG_Add("SHELL_CMD_DEL_ERROR","Unable to delete: %s.\n");
00618         MSG_Add("SHELL_CMD_DEL_SURE","Are you sure[Y,N]?");
00619         MSG_Add("SHELL_SYNTAXERROR","The syntax of the command is incorrect.\n");
00620         MSG_Add("SHELL_CMD_SET_NOT_SET","Environment variable %s not defined.\n");
00621         MSG_Add("SHELL_CMD_SET_OUT_OF_SPACE","Not enough environment space left.\n");
00622         MSG_Add("SHELL_CMD_IF_EXIST_MISSING_FILENAME","IF EXIST: Missing filename.\n");
00623         MSG_Add("SHELL_CMD_IF_ERRORLEVEL_MISSING_NUMBER","IF ERRORLEVEL: Missing number.\n");
00624         MSG_Add("SHELL_CMD_IF_ERRORLEVEL_INVALID_NUMBER","IF ERRORLEVEL: Invalid number.\n");
00625         MSG_Add("SHELL_CMD_GOTO_MISSING_LABEL","No label supplied to GOTO command.\n");
00626         MSG_Add("SHELL_CMD_GOTO_LABEL_NOT_FOUND","GOTO: Label %s not found.\n");
00627         MSG_Add("SHELL_CMD_FILE_NOT_FOUND","File %s not found.\n");
00628         MSG_Add("SHELL_CMD_FILE_EXISTS","File %s already exists.\n");
00629         MSG_Add("SHELL_CMD_DIR_INTRO"," Directory of %s.\n\n");
00630         MSG_Add("SHELL_CMD_DIR_BYTES_USED","%5d File(s) %17s Bytes.\n");
00631         MSG_Add("SHELL_CMD_DIR_BYTES_FREE","%5d Dir(s)  %17s Bytes free.\n");
00632         MSG_Add("SHELL_EXECUTE_DRIVE_NOT_FOUND","Drive %c does not exist!\nYou must \033[31mmount\033[0m it first. Type \033[1;33mintro\033[0m or \033[1;33mintro mount\033[0m for more information.\n");
00633         MSG_Add("SHELL_EXECUTE_DRIVE_ACCESS_CDROM","Do you want to give DOSBox access to your real CD-ROM drive %c [Y/N]?");
00634         MSG_Add("SHELL_EXECUTE_DRIVE_ACCESS_FLOPPY","Do you want to give DOSBox access to your real floppy drive %c [Y/N]?");
00635         MSG_Add("SHELL_EXECUTE_DRIVE_ACCESS_FIXED","Do you really want to give DOSBox access to everything\non your real drive %c [Y/N]?");
00636         MSG_Add("SHELL_EXECUTE_DRIVE_ACCESS_WARNING_WIN","Mounting c:\\ is NOT recommended.\n");
00637         MSG_Add("SHELL_EXECUTE_ILLEGAL_COMMAND","Illegal command: %s.\n");
00638         MSG_Add("SHELL_CMD_PAUSE","Press any key to continue.\n");
00639         MSG_Add("SHELL_CMD_PAUSE_HELP","Waits for 1 keystroke to continue.\n");
00640         MSG_Add("SHELL_CMD_COPY_FAILURE","Copy failure : %s.\n");
00641         MSG_Add("SHELL_CMD_COPY_SUCCESS","   %d File(s) copied.\n");
00642         MSG_Add("SHELL_CMD_SUBST_NO_REMOVE","Unable to remove, drive not in use.\n");
00643         MSG_Add("SHELL_CMD_SUBST_FAILURE","SUBST failed. You either made an error in your commandline or the target drive is already used.\nIt's only possible to use SUBST on Local drives\n");
00644 
00645     if (machine == MCH_PC98) {
00646         MSG_Add("SHELL_STARTUP_BEGIN",
00647                 "\033[44;1m\x86\x52\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44"
00648                 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44"
00649                 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44"
00650                 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44"
00651                 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44"
00652                 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x56\n"
00653                 "\x86\x46 \033[32mWelcome to DOSBox-X %-8s %-25s\033[37m             \x86\x46\n"
00654                 "\x86\x46                                                                    \x86\x46\n"
00655                 "\x86\x46 For a short introduction for new users type: \033[33mINTRO\033[37m                 \x86\x46\n"
00656                 "\x86\x46 For supported shell commands type: \033[33mHELP\033[37m                            \x86\x46\n"
00657                 "\x86\x46                                                                    \x86\x46\n"
00658                 "\x86\x46 To adjust the emulated CPU speed, use \033[31mhost -\033[37m and \033[31mhost +\033[37m.           \x86\x46\n"
00659                 "\x86\x46 To activate the keymapper \033[31mhost+m\033[37m. Host key is F11.                 \x86\x46\n"
00660                 "\x86\x46 For more information read the \033[36mREADME\033[37m file in the DOSBox directory. \x86\x46\n"
00661                 "\x86\x46                                                                    \x86\x46\n"
00662                );
00663         MSG_Add("SHELL_STARTUP_PC98","\x86\x46 DOSBox-X is now running in NEC PC-98 emulation mode.               \x86\x46\n"
00664                 "\x86\x46 \033[31mPC-98 emulation is INCOMPLETE and CURRENTLY IN DEVELOPMENT.\033[37m        \x86\x46\n");
00665         MSG_Add("SHELL_STARTUP_DEBUG",
00666 #if defined(MACOSX)
00667                 "\x86\x46 Debugger is available, use \033[31malt-F12\033[37m to enter.                       \x86\x46\n"
00668 #else
00669                 "\x86\x46 Debugger is available, use \033[31malt-Pause\033[37m to enter.                     \x86\x46\n"
00670 #endif
00671                 "\x86\x46                                                                    \x86\x46\n"
00672                );
00673         MSG_Add("SHELL_STARTUP_END",
00674                 "\x86\x46 \033[32mDOSBox-X project \033[33mhttp://dosbox-x.software\033[32m      PentiumPro support \033[37m \x86\x46\n"
00675                 "\x86\x46 \033[32mDerived from DOSBox \033[33mhttp://www.dosbox.com\033[37m                          \x86\x46\n"
00676                 "\x86\x5A\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44"
00677                 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44"
00678                 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44"
00679                 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44"
00680                 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44"
00681                 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x5E\033[0m\n"
00682                 "\033[1m\033[32mHAVE FUN!\033[0m\n"
00683                );
00684     }
00685     else {
00686         MSG_Add("SHELL_STARTUP_BEGIN",
00687                 "\033[44;1m\xC9\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
00688                 "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
00689                 "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBB\n"
00690                 "\xBA \033[32mWelcome to DOSBox-X %-8s %-25s\033[37m             \xBA\n"
00691                 "\xBA                                                                    \xBA\n"
00692                 "\xBA For a short introduction for new users type: \033[33mINTRO\033[37m                 \xBA\n"
00693                 "\xBA For supported shell commands type: \033[33mHELP\033[37m                            \xBA\n"
00694                 "\xBA                                                                    \xBA\n"
00695                 "\xBA To adjust the emulated CPU speed, use \033[31mhost -\033[37m and \033[31mhost +\033[37m.           \xBA\n"
00696                 "\xBA To activate the keymapper \033[31mhost+m\033[37m. Host key is F11.                 \xBA\n"
00697                 "\xBA For more information read the \033[36mREADME\033[37m file in the DOSBox directory. \xBA\n"
00698                 "\xBA                                                                    \xBA\n"
00699                );
00700         if (!mono_cga) {
00701             MSG_Add("SHELL_STARTUP_CGA","\xBA DOSBox supports Composite CGA mode.                                \xBA\n"
00702                     "\xBA Use \033[31mF12\033[37m to set composite output ON, OFF, or AUTO (default).        \xBA\n"
00703                     "\xBA \033[31m(Alt-)F11\033[37m changes hue; \033[31mctrl-alt-F11\033[37m selects early/late CGA model.  \xBA\n"
00704                     "\xBA                                                                    \xBA\n"
00705                    );
00706         } else {
00707             MSG_Add("SHELL_STARTUP_CGA","\xBA Use \033[31mF11\033[37m to cycle through green, amber, and white monochrome color, \xBA\n"
00708                     "\xBA and \033[31mAlt-F11\033[37m to change contrast/brightness settings.                \xBA\n"
00709                    );
00710         }
00711         MSG_Add("SHELL_STARTUP_PC98","\xBA DOSBox-X is now running in NEC PC-98 emulation mode.               \xBA\n"
00712                 "\xBA \033[31mPC-98 emulation is INCOMPLETE and CURRENTLY IN DEVELOPMENT.\033[37m        \xBA\n");
00713         MSG_Add("SHELL_STARTUP_HERC","\xBA Use F11 to cycle through white, amber, and green monochrome color. \xBA\n"
00714                 "\xBA Use alt-F11 to toggle horizontal blending (only in graphics mode). \xBA\n"
00715                 "\xBA                                                                    \xBA\n"
00716                );
00717         MSG_Add("SHELL_STARTUP_DEBUG",
00718 #if defined(MACOSX)
00719                 "\xBA Debugger is available, use \033[31malt-F12\033[37m to enter.                       \xBA\n"
00720 #else
00721                 "\xBA Debugger is available, use \033[31malt-Pause\033[37m to enter.                     \xBA\n"
00722 #endif
00723                 "\xBA                                                                    \xBA\n"
00724                );
00725         MSG_Add("SHELL_STARTUP_END",
00726                 "\xBA \033[32mDOSBox-X project \033[33mhttp://dosbox-x.software\033[32m      PentiumPro support \033[37m \xBA\n"
00727                 "\xBA \033[32mDerived from DOSBox \033[33mhttp://www.dosbox.com\033[37m                          \xBA\n"
00728                 "\xC8\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
00729                 "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD"
00730                 "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBC\033[0m\n"
00731                 "\033[1m\033[32mHAVE FUN!\033[0m\n"
00732                );
00733     }
00734 
00735         MSG_Add("SHELL_CMD_CHDIR_HELP","Displays/changes the current directory.\n");
00736         MSG_Add("SHELL_CMD_CHDIR_HELP_LONG","CHDIR [drive:][path]\n"
00737                 "CHDIR [..]\n"
00738                 "CD [drive:][path]\n"
00739                 "CD [..]\n\n"
00740                 "  ..   Specifies that you want to change to the parent directory.\n\n"
00741                 "Type CD drive: to display the current directory in the specified drive.\n"
00742                 "Type CD without parameters to display the current drive and directory.\n");
00743         MSG_Add("SHELL_CMD_CLS_HELP","Clear screen.\n");
00744         MSG_Add("SHELL_CMD_DIR_HELP","Directory View.\n");
00745         MSG_Add("SHELL_CMD_DIR_HELP_LONG","DIR [drive:][path][filename] [/[W|B]] [/S] [/P] [/AD] [/O[N|E|S|D]\n\n"
00746                    "   [drive:][path][filename]\n"
00747                    "       Specifies drive, directory, and/or files to list.\n\n"
00748                    "   /W\tUses wide list format.\n"
00749                    "   /B\tUses bare format (no heading information or summary).\n"
00750                    "   /S\tDisplays files in specified directory and all subdirectories.\n\t(not supported)\n"
00751                    "   /P\tPauses after each screenful of information.\n"
00752                    "   /AD\tDisplays directories.\n"
00753                    "   /ON\tList files sorted by name (alphabetic).\n"
00754                    "   /OE\tList files sorted by extension (alphabetic).\n"
00755                    "   /OS\tList files sorted by size (smallest first).\n"
00756                    "   /OD\tList files sorted by date (oldest first).\n");
00757         MSG_Add("SHELL_CMD_ECHO_HELP","Display messages and enable/disable command echoing.\n");
00758         MSG_Add("SHELL_CMD_EXIT_HELP","Exit from the shell.\n");
00759         MSG_Add("SHELL_CMD_HELP_HELP","Show help.\n");
00760 //      MSG_Add("SHELL_CMD_HELP_HELP_LONG","HELP [command]\n");
00761         MSG_Add("SHELL_CMD_MKDIR_HELP","Make Directory.\n");
00762         MSG_Add("SHELL_CMD_MKDIR_HELP_LONG","MKDIR [drive:][path]\n"
00763                 "MD [drive:][path]\n");
00764         MSG_Add("SHELL_CMD_RMDIR_HELP","Remove Directory.\n");
00765         MSG_Add("SHELL_CMD_RMDIR_HELP_LONG","RMDIR [drive:][path]\n"
00766                 "RD [drive:][path]\n");
00767         MSG_Add("SHELL_CMD_SET_HELP","Change environment variables.\n");
00768         MSG_Add("SHELL_CMD_SET_HELP_LONG","SET [variable=[string]]\n\n"
00769                    "   variable\tSpecifies the environment-variable name.\n"
00770                    "   string\tSpecifies a series of characters to assign to the variable.\n\n"
00771                    "* If no string is specified, the variable is removed from the environment.\n\n"
00772                    "Type SET without parameters to display the current environment variables.\n");
00773         MSG_Add("SHELL_CMD_IF_HELP","Performs conditional processing in batch programs.\n");
00774         MSG_Add("SHELL_CMD_IF_HELP_LONG","IF [NOT] ERRORLEVEL number command\n"
00775                    "IF [NOT] string1==string2 command\n"
00776                    "IF [NOT] EXIST filename command\n\n"
00777                    "  NOT               Specifies that DOS should carry out\n"
00778                    "                    the command only if the condition is false.\n\n"
00779                    "  ERRORLEVEL number Specifies a true condition if the last program run\n"
00780                    "                    returned an exit code equal to or greater than the number\n"
00781                    "                    specified.\n\n"
00782                    "  string1==string2  Specifies a true condition if the specified text strings\n"
00783                    "                    match.\n\n"
00784                    "  EXIST filename    Specifies a true condition if the specified filename\n"
00785                    "                    exists.\n\n"
00786                    "  command           Specifies the command to carry out if the condition is\n"
00787                    "                    met.  Command can be followed by ELSE command which\n"
00788                    "                    will execute the command after the ELSE keyword if the\n"
00789                    "                    specified condition is FALSE\n");
00790         MSG_Add("SHELL_CMD_GOTO_HELP","Jump to a labeled line in a batch script.\n");
00791         MSG_Add("SHELL_CMD_GOTO_HELP_LONG","GOTO label\n\n"
00792                    "   label   Specifies a text string used in the batch program as a label.\n\n"
00793                    "You type a label on a line by itself, beginning with a colon.\n");
00794         MSG_Add("SHELL_CMD_SHIFT_HELP","Leftshift commandline parameters in a batch script.\n");
00795         MSG_Add("SHELL_CMD_FOR_HELP","Does nothing. Provided for compatibility.\n");
00796         MSG_Add("SHELL_CMD_TYPE_HELP","Display the contents of a text-file.\n");
00797         MSG_Add("SHELL_CMD_TYPE_HELP_LONG","TYPE [drive:][path][filename]\n");
00798         MSG_Add("SHELL_CMD_REM_HELP","Add comments in a batch file.\n");
00799         MSG_Add("SHELL_CMD_REM_HELP_LONG","REM [comment]\n");
00800         MSG_Add("SHELL_CMD_NO_WILD","This is a simple version of the command, no wildcards allowed!\n");
00801         MSG_Add("SHELL_CMD_RENAME_HELP","Renames one or more files.\n");
00802         MSG_Add("SHELL_CMD_RENAME_HELP_LONG","RENAME [drive:][path]filename1 filename2.\n"
00803                 "REN [drive:][path]filename1 filename2.\n\n"
00804                 "Note that you can not specify a new drive or path for your destination file.\n");
00805         MSG_Add("SHELL_CMD_DELETE_HELP","Removes one or more files.\n");
00806         MSG_Add("SHELL_CMD_COPY_HELP","Copy files.\n");
00807         MSG_Add("SHELL_CMD_DELETE_HELP_LONG","DEL [/P] [/Q] names\n"
00808                    "ERASE [/P] [/Q] names\n\n"
00809                    "  names\t\tSpecifies a list of one or more files or directories.\n"
00810                    "\t\tWildcards may be used to delete multiple files. If a\n"
00811                    "\t\tdirectory is specified, all files within the directory\n"
00812                    "\t\twill be deleted.\n"
00813                    "  /P\t\tPrompts for confirmation before deleting one or more files.\n"
00814                    "  /Q\t\tQuiet mode, do not ask if ok to delete on global wildcard\n");
00815         MSG_Add("SHELL_CMD_CALL_HELP","Start a batch file from within another batch file.\n");
00816         MSG_Add("SHELL_CMD_CALL_HELP_LONG","CALL [drive:][path]filename [batch-parameters]\n\n"
00817                    "batch-parameters   Specifies any command-line information required by\n"
00818                    "                   the batch program.\n");
00819         MSG_Add("SHELL_CMD_SUBST_HELP","Assign an internal directory to a drive.\n");
00820         MSG_Add("SHELL_CMD_LOADHIGH_HELP","Loads a program into upper memory (requires xms=true,umb=true).\n");
00821         MSG_Add("SHELL_CMD_CHOICE_HELP","Waits for a keypress and sets ERRORLEVEL.\n");
00822         MSG_Add("SHELL_CMD_CHOICE_HELP_LONG","CHOICE [/C:choices] [/N] [/S] text\n"
00823                 "  /C[:]choices  -  Specifies allowable keys.  Default is: yn.\n"
00824                 "  /N  -  Do not display the choices at end of prompt.\n"
00825                 "  /S  -  Enables case-sensitive choices to be selected.\n"
00826                 "  text  -  The text to display as a prompt.\n");
00827         MSG_Add("SHELL_CMD_ATTRIB_HELP","Does nothing. Provided for compatibility.\n");
00828         MSG_Add("SHELL_CMD_PATH_HELP","Displays/Sets a search path for executable files.\n");
00829         MSG_Add("SHELL_CMD_PATH_HELP_LONG","PATH [[drive:]path[;...][;%PATH%]\n"
00830                    "PATH ;\n\n"
00831                    "Type PATH ; to clear all search path settings.\n"
00832                    "Type PATH without parameters to display the current path.\n");
00833         MSG_Add("SHELL_CMD_VER_HELP","View and set the reported DOS version.\n");
00834         MSG_Add("SHELL_CMD_VER_HELP_LONG","VER\n" 
00835                    "VER SET [major minor]\n\n" 
00836                    "  major minor   Set the reported DOS version. (e.g. VER SET 5 1)\n\n" 
00837                    "Type VER without parameters to display the current DOS version.\n");
00838         MSG_Add("SHELL_CMD_VER_VER","DOSBox version %s. Reported DOS version %d.%02d.\n");
00839         MSG_Add("SHELL_CMD_ADDKEY_HELP","Generates artificial keypresses.\n");
00840         MSG_Add("SHELL_CMD_VOL_HELP","Displays the disk volume label and serial number, if they exist.\n");
00841         MSG_Add("SHELL_CMD_VOL_HELP_LONG","VOL [drive]\n");
00842         MSG_Add("SHELL_CMD_PROMPT_HELP","Change the command prompt.\n");
00843         MSG_Add("SHELL_CMD_PROMPT_HELP_LONG","PROMPT [text]\n"
00844                    "  text    Specifies a new command prompt.\n\n"
00845                    "Prompt can be made up of normal characters and the following special codes:\n"
00846                    "  $A   & (Ampersand)\n"
00847                    "  $B   | (pipe)\n"
00848                    "  $C   ( (Left parenthesis)\n"
00849                    "  $D   Current date\n"
00850                    "  $E   Escape code (ASCII code 27)\n"
00851                    "  $F   ) (Right parenthesis)\n"
00852                    "  $G   > (greater-than sign)\n"
00853                    "  $H   Backspace (erases previous character)\n"
00854                    "  $L   < (less-than sign)\n"
00855                    "  $N   Current drive\n"
00856                    "  $P   Current drive and path\n"
00857                    "  $Q   = (equal sign)\n"
00858                    "  $S     (space)\n"
00859                    "  $T   Current time\n"
00860                    "  $V   DOS version number\n"
00861                    "  $_   Carriage return and linefeed\n"
00862                    "  $$   $ (dollar sign)\n");
00863         MSG_Add("SHELL_CMD_LABEL_HELP","Creates or changes the volume label of a disk.\n");
00864         MSG_Add("SHELL_CMD_LABEL_HELP_LONG","LABEL [volume]\n\n\tvolume\t\tSpecifies the drive letter.\n");
00865         MSG_Add("SHELL_CMD_MORE_HELP","Displays output one screen at a time.\n");
00866         MSG_Add("SHELL_CMD_MORE_HELP_LONG","MORE [filename]\n");
00867 
00868         /* Regular startup */
00869         call_shellstop=CALLBACK_Allocate();
00870         /* Setup the startup CS:IP to kill the last running machine when exitted */
00871         RealPt newcsip=CALLBACK_RealPointer(call_shellstop);
00872         SegSet16(cs,RealSeg(newcsip));
00873         reg_ip=RealOff(newcsip);
00874 
00875         CALLBACK_Setup(call_shellstop,shellstop_handler,CB_IRET,"shell stop");
00876         PROGRAMS_MakeFile("COMMAND.COM",SHELL_ProgramStart);
00877 
00878     /* NTS: Some DOS programs behave badly if run from a command interpreter
00879      *      who's PSP segment is too low in memory and does not appear in
00880      *      the MCB chain (SimCity 2000). So allocate shell memory normally
00881      *      as any DOS application would do.
00882      *
00883      *      That includes allocating COMMAND.COM stack NORMALLY (not up in
00884      *      the UMB as DOSBox SVN would do) */
00885 
00886         /* Now call up the shell for the first time */
00887         Bit16u psp_seg;//=DOS_FIRST_SHELL;
00888         Bit16u env_seg;//=DOS_FIRST_SHELL+19; //DOS_GetMemory(1+(4096/16))+1;
00889         Bit16u stack_seg;//=DOS_GetMemory(2048/16,"COMMAND.COM stack");
00890     Bit16u tmp,total_sz;
00891 
00892     // decide shell env size
00893     if (dosbox_shell_env_size == 0)
00894         dosbox_shell_env_size = (0x158u - (0x118u + 19u)) << 4u; /* equivalent to mainline DOSBox */
00895     else
00896         dosbox_shell_env_size = (dosbox_shell_env_size+15u)&(~15u); /* round up to paragraph */
00897 
00898     LOG_MSG("COMMAND.COM env size:             %u bytes",dosbox_shell_env_size);
00899 
00900     // According to some sources, 0x0008 is a special PSP segment value used by DOS before the first
00901     // program is used. We need the current PSP segment to be nonzero so that DOS_AllocateMemory()
00902     // can properly allocate memory.
00903     dos.psp(8);
00904 
00905     // COMMAND.COM environment block
00906     tmp = dosbox_shell_env_size>>4;
00907         if (!DOS_AllocateMemory(&env_seg,&tmp)) E_Exit("COMMAND.COM failed to allocate environment block segment");
00908     LOG_MSG("COMMAND.COM environment block:    0x%04x sz=0x%04x",env_seg,tmp);
00909 
00910     // COMMAND.COM main binary (including PSP and stack)
00911     tmp = 0x1A + (2048/16);
00912     total_sz = tmp;
00913         if (!DOS_AllocateMemory(&psp_seg,&tmp)) E_Exit("COMMAND.COM failed to allocate main body + PSP segment");
00914     LOG_MSG("COMMAND.COM main body (PSP):      0x%04x sz=0x%04x",psp_seg,tmp);
00915 
00916     // now COMMAND.COM has a main body and PSP segment, reflect it
00917     dos.psp(psp_seg);
00918 
00919     {
00920         DOS_MCB mcb((Bit16u)(env_seg-1));
00921         mcb.SetPSPSeg(psp_seg);
00922         mcb.SetFileName("COMMAND");
00923     }
00924 
00925     {
00926         DOS_MCB mcb((Bit16u)(psp_seg-1));
00927         mcb.SetPSPSeg(psp_seg);
00928         mcb.SetFileName("COMMAND");
00929     }
00930 
00931     // set the stack at 0x1A
00932     stack_seg = psp_seg + 0x1A;
00933     LOG_MSG("COMMAND.COM stack:                0x%04x",stack_seg);
00934 
00935     // set the stack pointer
00936         SegSet16(ss,stack_seg);
00937         reg_sp=2046;
00938 
00939         /* Set up int 24 and psp (Telarium games) */
00940         real_writeb(psp_seg+16+1,0,0xea);               /* far jmp */
00941         real_writed(psp_seg+16+1,1,real_readd(0,0x24*4));
00942         real_writed(0,0x24*4,((Bit32u)psp_seg<<16) | ((16+1)<<4));
00943 
00944         /* Set up int 23 to "int 20" in the psp. Fixes what.exe */
00945         real_writed(0,0x23*4,((Bit32u)psp_seg<<16));
00946         
00947         /* Setup environment */
00948         PhysPt env_write=PhysMake(env_seg,0);
00949         MEM_BlockWrite(env_write,path_string,(Bitu)(strlen(path_string)+1));
00950         env_write += (PhysPt)(strlen(path_string)+1);
00951         MEM_BlockWrite(env_write,comspec_string,(Bitu)(strlen(comspec_string)+1));
00952         env_write += (PhysPt)(strlen(comspec_string)+1);
00953         MEM_BlockWrite(env_write,prompt_string,(Bitu)(strlen(prompt_string)+1));
00954         env_write +=(PhysPt)(strlen(prompt_string)+1);
00955         mem_writeb(env_write++,0);
00956         mem_writew(env_write,1);
00957         env_write+=2;
00958         MEM_BlockWrite(env_write,full_name,(Bitu)(strlen(full_name)+1));
00959 
00960 //    extern bool Mouse_Vertical;
00961         extern bool Mouse_Drv;
00962         Mouse_Drv = true;
00963 
00964         VFILE_RegisterBuiltinFileBlob(bfb_DEBUG_EXE);
00965         VFILE_RegisterBuiltinFileBlob(bfb_MOVE_EXE);
00966         VFILE_RegisterBuiltinFileBlob(bfb_FIND_EXE);
00967         VFILE_RegisterBuiltinFileBlob(bfb_LASTDRIV_COM);
00968         VFILE_RegisterBuiltinFileBlob(bfb_FCBS_COM);
00969         VFILE_RegisterBuiltinFileBlob(bfb_XCOPY_EXE);
00970         VFILE_RegisterBuiltinFileBlob(bfb_APPEND_EXE);
00971         VFILE_RegisterBuiltinFileBlob(bfb_DEVICE_COM);
00972         VFILE_RegisterBuiltinFileBlob(bfb_BUFFERS_COM);
00973         VFILE_RegisterBuiltinFileBlob(bfb_COPY_EXE);
00974 
00975     /* These are IBM PC/XT/AT ONLY. They will not work in PC-98 mode. */
00976     if (!IS_PC98_ARCH) {
00977         VFILE_RegisterBuiltinFileBlob(bfb_HEXMEM16_EXE);
00978         VFILE_RegisterBuiltinFileBlob(bfb_HEXMEM32_EXE);
00979         VFILE_RegisterBuiltinFileBlob(bfb_DOSIDLE_EXE);
00980         VFILE_RegisterBuiltinFileBlob(bfb_CWSDPMI_EXE);
00981         VFILE_RegisterBuiltinFileBlob(bfb_DOS32A_EXE);
00982         VFILE_RegisterBuiltinFileBlob(bfb_DOS4GW_EXE);
00983         VFILE_RegisterBuiltinFileBlob(bfb_EDIT_COM);
00984         VFILE_RegisterBuiltinFileBlob(bfb_TREE_EXE);
00985         VFILE_RegisterBuiltinFileBlob(bfb_MEM_COM);
00986         VFILE_RegisterBuiltinFileBlob(bfb_25_COM);
00987     }
00988 
00989         /* don't register 28.com unless EGA/VGA */
00990         if (IS_EGAVGA_ARCH) VFILE_RegisterBuiltinFileBlob(bfb_28_COM);
00991 
00992         /* don't register 50 unless VGA */
00993         if (IS_VGA_ARCH) VFILE_RegisterBuiltinFileBlob(bfb_50_COM);
00994 
00995         DOS_PSP psp(psp_seg);
00996         psp.MakeNew(0);
00997         dos.psp(psp_seg);
00998    
00999         /* The start of the filetable in the psp must look like this:
01000          * 01 01 01 00 02
01001          * In order to achieve this: First open 2 files. Close the first and
01002          * duplicate the second (so the entries get 01) */
01003         Bit16u dummy=0;
01004         DOS_OpenFile("CON",OPEN_READWRITE,&dummy);      /* STDIN  */
01005         DOS_OpenFile("CON",OPEN_READWRITE,&dummy);      /* STDOUT */
01006         DOS_CloseFile(0);                                                       /* Close STDIN */
01007         DOS_ForceDuplicateEntry(1,0);                           /* "new" STDIN */
01008         DOS_ForceDuplicateEntry(1,2);                           /* STDERR */
01009         DOS_OpenFile("CON",OPEN_READWRITE,&dummy);      /* STDAUX */
01010         if (!DOS_OpenFile("PRN",OPEN_READWRITE,&dummy)) DOS_OpenFile("CON",OPEN_READWRITE,&dummy);      /* STDPRN */
01011 
01012     psp.SetSize(psp_seg + total_sz);
01013     psp.SetStack(((unsigned int)stack_seg << 16u) + (unsigned int)reg_sp);
01014         psp.SetParent(psp_seg);
01015         /* Set the environment */
01016         psp.SetEnvironment(env_seg);
01017         /* Set the command line for the shell start up */
01018         CommandTail tail;
01019         tail.count=(Bit8u)strlen(init_line);
01020         strcpy(tail.buffer,init_line);
01021         MEM_BlockWrite(PhysMake(psp_seg,128),&tail,128);
01022         
01023         /* Setup internal DOS Variables */
01024         dos.dta(RealMake(psp_seg,0x80));
01025         dos.psp(psp_seg);
01026 }
01027 
01028 /* Pfff... starting and running the shell from a configuration section INIT
01029  * What the hell were you guys thinking? --J.C. */
01030 void SHELL_Run() {
01031         dos_shell_running_program = false;
01032 #if defined(WIN32) && !defined(C_SDL2)
01033         int Reflect_Menu(void);
01034         Reflect_Menu();
01035 #endif
01036 
01037         LOG(LOG_MISC,LOG_DEBUG)("Running DOS shell now");
01038 
01039         if (first_shell != NULL) E_Exit("Attempt to start shell when shell already running");
01040         SHELL_ProgramStart(&first_shell);
01041 
01042         try {
01043                 first_shell->Run();
01044                 delete first_shell;
01045                 first_shell = 0;//Make clear that it shouldn't be used anymore
01046                 dos_shell_running_program = false;
01047 #if defined(WIN32) && !defined(C_SDL2)
01048                 int Reflect_Menu(void);
01049                 Reflect_Menu();
01050 #endif
01051         }
01052         catch (...) {
01053                 delete first_shell;
01054                 first_shell = 0;//Make clear that it shouldn't be used anymore
01055                 dos_shell_running_program = false;
01056 #if defined(WIN32) && !defined(C_SDL2)
01057                 int Reflect_Menu(void);
01058                 Reflect_Menu();
01059 #endif
01060                 throw;
01061         }
01062 }
01063