DOSBox-X
|
00001 /* 00002 * Copyright (C) 2002-2020 The DOSBox Team 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License along 00015 * with this program; if not, write to the Free Software Foundation, Inc., 00016 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 */ 00018 00019 #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 "cpu.h" 00029 #include "callback.h" 00030 #include "support.h" 00031 #include "builtin.h" 00032 #include "mapper.h" 00033 #include "../dos/drives.h" 00034 #include <unistd.h> 00035 #include <time.h> 00036 #include <string> 00037 #include <sstream> 00038 #include "build_timestamp.h" 00039 00040 extern bool enable_config_as_shell_commands; 00041 extern bool dos_shell_running_program; 00042 extern Bit16u countryNo; 00043 bool usecon = true; 00044 00045 Bit16u shell_psp = 0; 00046 Bitu call_int2e = 0; 00047 00048 void MSG_Replace(const char * _name, const char* _val); 00049 void DOS_SetCountry(Bit16u countryNo); 00050 void CALLBACK_DeAllocate(Bitu in); 00051 00052 Bitu call_shellstop = 0; 00053 /* Larger scope so shell_del autoexec can use it to 00054 * remove things from the environment */ 00055 DOS_Shell * first_shell = 0; 00056 00057 static Bitu shellstop_handler(void) { 00058 return CBRET_STOP; 00059 } 00060 00061 static void SHELL_ProgramStart(Program * * make) { 00062 *make = new DOS_Shell; 00063 } 00064 //Repeat it with the correct type, could do it in the function below, but this way it should be 00065 //clear that if the above function is changed, this function might need a change as well. 00066 static void SHELL_ProgramStart_First_shell(DOS_Shell * * make) { 00067 *make = new DOS_Shell; 00068 } 00069 00070 #define CONFIG_SIZE 4096 00071 #define AUTOEXEC_SIZE 4096 00072 static char config_data[CONFIG_SIZE] = { 0 }; 00073 static char autoexec_data[AUTOEXEC_SIZE] = { 0 }; 00074 static std::list<std::string> autoexec_strings; 00075 typedef std::list<std::string>::iterator auto_it; 00076 00077 void VFILE_Remove(const char *name); 00078 00079 void AutoexecObject::Install(const std::string &in) { 00080 if(GCC_UNLIKELY(installed)) E_Exit("autoexec: already created %s",buf.c_str()); 00081 installed = true; 00082 buf = in; 00083 autoexec_strings.push_back(buf); 00084 this->CreateAutoexec(); 00085 00086 //autoexec.bat is normally created AUTOEXEC_Init. 00087 //But if we are already running (first_shell) 00088 //we have to update the envirionment to display changes 00089 00090 if(first_shell) { 00091 //create a copy as the string will be modified 00092 std::string::size_type n = buf.size(); 00093 char* buf2 = new char[n + 1]; 00094 safe_strncpy(buf2, buf.c_str(), n + 1); 00095 if((strncasecmp(buf2,"set ",4) == 0) && (strlen(buf2) > 4)){ 00096 char* after_set = buf2 + 4;//move to variable that is being set 00097 char* test2 = strpbrk(after_set,"="); 00098 if(!test2) {first_shell->SetEnv(after_set,"");return;} 00099 *test2++ = 0; 00100 //If the shell is running/exists update the environment 00101 first_shell->SetEnv(after_set,test2); 00102 } 00103 delete [] buf2; 00104 } 00105 } 00106 00107 void AutoexecObject::InstallBefore(const std::string &in) { 00108 if(GCC_UNLIKELY(installed)) E_Exit("autoexec: already created %s",buf.c_str()); 00109 installed = true; 00110 buf = in; 00111 autoexec_strings.push_front(buf); 00112 this->CreateAutoexec(); 00113 } 00114 00115 void AutoexecObject::CreateAutoexec(void) { 00116 /* Remove old autoexec.bat if the shell exists */ 00117 if(first_shell) VFILE_Remove("AUTOEXEC.BAT"); 00118 00119 //Create a new autoexec.bat 00120 autoexec_data[0] = 0; 00121 size_t auto_len; 00122 for(auto_it it = autoexec_strings.begin(); it != autoexec_strings.end(); ++it) { 00123 00124 std::string linecopy = *it; 00125 std::string::size_type offset = 0; 00126 //Lets have \r\n as line ends in autoexec.bat. 00127 while(offset < linecopy.length()) { 00128 std::string::size_type n = linecopy.find("\n",offset); 00129 if ( n == std::string::npos ) break; 00130 std::string::size_type rn = linecopy.find("\r\n",offset); 00131 if ( rn != std::string::npos && rn + 1 == n) {offset = n + 1; continue;} 00132 // \n found without matching \r 00133 linecopy.replace(n,1,"\r\n"); 00134 offset = n + 2; 00135 } 00136 00137 auto_len = strlen(autoexec_data); 00138 if ((auto_len+linecopy.length() + 3) > AUTOEXEC_SIZE) { 00139 E_Exit("SYSTEM:Autoexec.bat file overflow"); 00140 } 00141 sprintf((autoexec_data + auto_len),"%s\r\n",linecopy.c_str()); 00142 } 00143 if (first_shell) VFILE_Register("AUTOEXEC.BAT",(Bit8u *)autoexec_data,(Bit32u)strlen(autoexec_data)); 00144 } 00145 00146 void AutoexecObject::Uninstall() { 00147 if(!installed) return; 00148 00149 // Remove the line from the autoexecbuffer and update environment 00150 for(auto_it it = autoexec_strings.begin(); it != autoexec_strings.end(); ) { 00151 if ((*it) == buf) { 00152 std::string::size_type n = buf.size(); 00153 char* buf2 = new char[n + 1]; 00154 safe_strncpy(buf2, buf.c_str(), n + 1); 00155 bool stringset = false; 00156 // If it's a environment variable remove it from there as well 00157 if ((strncasecmp(buf2,"set ",4) == 0) && (strlen(buf2) > 4)){ 00158 char* after_set = buf2 + 4;//move to variable that is being set 00159 char* test2 = strpbrk(after_set,"="); 00160 if (!test2) continue; 00161 *test2 = 0; 00162 stringset = true; 00163 //If the shell is running/exists update the environment 00164 if (first_shell) first_shell->SetEnv(after_set,""); 00165 } 00166 delete [] buf2; 00167 if (stringset && first_shell && first_shell->bf && first_shell->bf->filename.find("AUTOEXEC.BAT") != std::string::npos) { 00168 //Replace entry with spaces if it is a set and from autoexec.bat, as else the location counter will be off. 00169 *it = buf.assign(buf.size(),' '); 00170 ++it; 00171 } else { 00172 it = autoexec_strings.erase(it); 00173 } 00174 } else ++it; 00175 } 00176 installed=false; 00177 this->CreateAutoexec(); 00178 } 00179 00180 AutoexecObject::~AutoexecObject(){ 00181 Uninstall(); 00182 } 00183 00184 DOS_Shell::~DOS_Shell() { 00185 if (bf != NULL) delete bf; /* free batch file */ 00186 } 00187 00188 DOS_Shell::DOS_Shell():Program(){ 00189 input_handle=STDIN; 00190 echo=true; 00191 exit=false; 00192 bf=0; 00193 call=false; 00194 lfnfor = uselfn; 00195 input_eof=false; 00196 completion_index = 0; 00197 } 00198 00199 Bitu DOS_Shell::GetRedirection(char *s, char **ifn, char **ofn, char **toc,bool * append) { 00200 00201 char * lr=s; 00202 char * lw=s; 00203 char ch; 00204 Bitu num=0; 00205 bool quote = false; 00206 char* t; 00207 int q; 00208 00209 while ( (ch=*lr++) ) { 00210 if(quote && ch != '"') { /* don't parse redirection within quotes. Not perfect yet. Escaped quotes will mess the count up */ 00211 *lw++ = ch; 00212 continue; 00213 } 00214 00215 switch (ch) { 00216 case '"': 00217 quote = !quote; 00218 break; 00219 case '>': 00220 *append = ((*lr) == '>'); 00221 if (*append) 00222 lr++; 00223 lr = ltrim(lr); 00224 if (*ofn) 00225 free(*ofn); 00226 *ofn = lr; 00227 q = 0; 00228 while (*lr && (q/2*2!=q || *lr != ' ') && *lr != '<' && *lr != '|') { 00229 if (*lr=='"') 00230 q++; 00231 lr++; 00232 } 00233 // if it ends on a : => remove it. 00234 if ((*ofn != lr) && (lr[-1] == ':')) 00235 lr[-1] = 0; 00236 t = (char*)malloc(lr-*ofn+1); 00237 safe_strncpy(t, *ofn, lr-*ofn+1); 00238 *ofn = t; 00239 continue; 00240 case '<': 00241 if (*ifn) 00242 free(*ifn); 00243 lr = ltrim(lr); 00244 *ifn = lr; 00245 q = 0; 00246 while (*lr && (q/2*2!=q || *lr != ' ') && *lr != '>' && *lr != '|') { 00247 if (*lr=='"') 00248 q++; 00249 lr++; 00250 } 00251 if ((*ifn != lr) && (lr[-1] == ':')) 00252 lr[-1] = 0; 00253 t = (char*)malloc(lr-*ifn+1); 00254 safe_strncpy(t, *ifn, lr-*ifn+1); 00255 *ifn = t; 00256 continue; 00257 case '|': 00258 num++; 00259 if (*toc) 00260 free(*toc); 00261 lr = ltrim(lr); 00262 *toc = lr; 00263 while (*lr) 00264 lr++; 00265 t = (char*)malloc(lr-*toc+1); 00266 safe_strncpy(t, *toc, lr-*toc+1); 00267 *toc = t; 00268 continue; 00269 } 00270 *lw++=ch; 00271 } 00272 *lw=0; 00273 return num; 00274 } 00275 00276 void DOS_Shell::ParseLine(char * line) { 00277 LOG(LOG_EXEC,LOG_DEBUG)("Parsing command line: %s",line); 00278 /* Check for a leading @ */ 00279 if (line[0] == '@') line[0] = ' '; 00280 line = trim(line); 00281 00282 /* Do redirection and pipe checks */ 00283 00284 char * in = 0; 00285 char * out = 0; 00286 char * toc = 0; 00287 00288 Bit16u dummy,dummy2; 00289 Bit32u bigdummy = 0; 00290 bool append; 00291 bool normalstdin = false; /* wether stdin/out are open on start. */ 00292 bool normalstdout = false; /* Bug: Assumed is they are "con" */ 00293 00294 GetRedirection(line, &in, &out, &toc, &append); 00295 if (in || out || toc) { 00296 normalstdin = (psp->GetFileHandle(0) != 0xff); 00297 normalstdout = (psp->GetFileHandle(1) != 0xff); 00298 } 00299 if (in) { 00300 if(DOS_OpenFile(in,OPEN_READ,&dummy)) { //Test if file exists 00301 DOS_CloseFile(dummy); 00302 LOG_MSG("SHELL:Redirect input from %s",in); 00303 if(normalstdin) DOS_CloseFile(0); //Close stdin 00304 DOS_OpenFile(in,OPEN_READ,&dummy); //Open new stdin 00305 } else 00306 WriteOut(!*in?"File open error\n":(dos.errorcode==DOSERR_ACCESS_DENIED?"Access denied - %s\n":"File open error - %s\n"), in); 00307 } 00308 bool fail=false; 00309 char pipetmp[270]; 00310 Bit16u fattr; 00311 if (toc) { 00312 #ifdef WIN32 00313 srand(GetTickCount()); 00314 #else 00315 struct timespec ts; 00316 unsigned theTick = 0U; 00317 clock_gettime( CLOCK_REALTIME, &ts ); 00318 theTick = ts.tv_nsec / 1000000; 00319 theTick += ts.tv_sec * 1000; 00320 srand(theTick); 00321 #endif 00322 std::string line; 00323 if (!GetEnvStr("TEMP",line)&&!GetEnvStr("TMP",line)) 00324 sprintf(pipetmp, "pipe%d.tmp", rand()%10000); 00325 else { 00326 std::string::size_type idx = line.find('='); 00327 std::string temp=line.substr(idx +1 , std::string::npos); 00328 if (DOS_GetFileAttr(temp.c_str(), &fattr) && fattr&DOS_ATTR_DIRECTORY) 00329 sprintf(pipetmp, "%s\\pipe%d.tmp", temp.c_str(), rand()%10000); 00330 else 00331 sprintf(pipetmp, "pipe%d.tmp", rand()%10000); 00332 } 00333 } 00334 if (out||toc) { 00335 if (out&&toc) 00336 WriteOut(!*out?"Duplicate redirection\n":"Duplicate redirection - %s\n", out); 00337 LOG_MSG("SHELL:Redirect output to %s",toc?pipetmp:out); 00338 if(normalstdout) DOS_CloseFile(1); 00339 if(!normalstdin && !in) DOS_OpenFile("con",OPEN_READWRITE,&dummy); 00340 bool status = true; 00341 /* Create if not exist. Open if exist. Both in read/write mode */ 00342 if(!toc&&append) { 00343 if (DOS_GetFileAttr(out, &fattr) && fattr&DOS_ATTR_READ_ONLY) { 00344 DOS_SetError(DOSERR_ACCESS_DENIED); 00345 status = false; 00346 } else if( (status = DOS_OpenFile(out,OPEN_READWRITE,&dummy)) ) { 00347 DOS_SeekFile(1,&bigdummy,DOS_SEEK_END); 00348 } else { 00349 status = DOS_CreateFile(out,DOS_ATTR_ARCHIVE,&dummy); //Create if not exists. 00350 } 00351 } else if (!toc&&DOS_GetFileAttr(out, &fattr) && fattr&DOS_ATTR_READ_ONLY) { 00352 DOS_SetError(DOSERR_ACCESS_DENIED); 00353 status = false; 00354 } else { 00355 if (toc&&DOS_FindFirst(pipetmp, ~DOS_ATTR_VOLUME)&&!DOS_UnlinkFile(pipetmp)) 00356 fail=true; 00357 status = DOS_OpenFileExtended(toc?pipetmp:out,OPEN_READWRITE,DOS_ATTR_ARCHIVE,0x12,&dummy,&dummy2); 00358 } 00359 00360 if(!status && normalstdout) { 00361 DOS_OpenFile("con", OPEN_READWRITE, &dummy); // Read only file, open con again 00362 if (!toc) { 00363 WriteOut(!*out?"File creation error\n":(dos.errorcode==DOSERR_ACCESS_DENIED?"Access denied - %s\n":"File creation error - %s\n"), out); 00364 DOS_CloseFile(1); 00365 DOS_OpenFile("nul", OPEN_READWRITE, &dummy); 00366 } 00367 } 00368 if(!normalstdin && !in) DOS_CloseFile(0); 00369 } 00370 /* Run the actual command */ 00371 00372 if (this == first_shell) dos_shell_running_program = true; 00373 #if defined(WIN32) && !defined(C_SDL2) 00374 int Reflect_Menu(void); 00375 Reflect_Menu(); 00376 #endif 00377 if (toc||(!toc&&((out&&DOS_FindDevice(out)!=DOS_FindDevice("con"))||(in&&DOS_FindDevice(in)!=DOS_FindDevice("con"))))) usecon=false; 00378 00379 DoCommand(line); 00380 00381 if (this == first_shell) dos_shell_running_program = false; 00382 #if defined(WIN32) && !defined(C_SDL2) 00383 int Reflect_Menu(void); 00384 Reflect_Menu(); 00385 #endif 00386 00387 /* Restore handles */ 00388 if(in) { 00389 DOS_CloseFile(0); 00390 if(normalstdin) DOS_OpenFile("con",OPEN_READWRITE,&dummy); 00391 free(in); 00392 } 00393 if(out||toc) { 00394 DOS_CloseFile(1); 00395 if(!normalstdin) DOS_OpenFile("con",OPEN_READWRITE,&dummy); 00396 if(normalstdout) DOS_OpenFile("con",OPEN_READWRITE,&dummy); 00397 if(!normalstdin) DOS_CloseFile(0); 00398 if (out) free(out); 00399 } 00400 if (toc) 00401 { 00402 if (!fail&&DOS_OpenFile(pipetmp, OPEN_READ, &dummy)) // Test if file can be opened for reading 00403 { 00404 DOS_CloseFile(dummy); 00405 if (normalstdin) 00406 DOS_CloseFile(0); // Close stdin 00407 DOS_OpenFile(pipetmp, OPEN_READ, &dummy); // Open new stdin 00408 ParseLine(toc); 00409 DOS_CloseFile(0); 00410 if (normalstdin) 00411 DOS_OpenFile("con", OPEN_READWRITE, &dummy); 00412 } 00413 else 00414 WriteOut("\nFailed to create/open a temporary file for piping. Check the %%TEMP%% variable.\n"); 00415 free(toc); 00416 if (DOS_FindFirst(pipetmp, ~DOS_ATTR_VOLUME)) DOS_UnlinkFile(pipetmp); 00417 } 00418 usecon=true; 00419 } 00420 00421 00422 00423 void DOS_Shell::RunInternal(void) { 00424 char input_line[CMD_MAXLINE] = {0}; 00425 while (bf) { 00426 if (bf->ReadLine(input_line)) { 00427 if (echo) { 00428 if (input_line[0] != '@') { 00429 ShowPrompt(); 00430 WriteOut_NoParsing(input_line); 00431 WriteOut_NoParsing("\n"); 00432 } 00433 } 00434 ParseLine(input_line); 00435 if (echo) WriteOut_NoParsing("\n"); 00436 } 00437 } 00438 } 00439 00440 void DOS_Shell::Run(void) { 00441 char input_line[CMD_MAXLINE] = {0}; 00442 std::string line; 00443 bool optC=cmd->FindStringRemainBegin("/C",line), optK=false; 00444 if (!optC) optK=cmd->FindStringRemainBegin("/K",line); 00445 if (optC||optK) { 00446 input_line[CMD_MAXLINE-1u] = 0; 00447 strncpy(input_line,line.c_str(),CMD_MAXLINE-1u); 00448 char* sep = strpbrk(input_line,"\r\n"); //GTA installer 00449 if (sep) *sep = 0; 00450 DOS_Shell temp; 00451 temp.echo = echo; 00452 temp.ParseLine(input_line); //for *.exe *.com |*.bat creates the bf needed by runinternal; 00453 temp.RunInternal(); // exits when no bf is found. 00454 if (!optK||temp.exit) 00455 return; 00456 } else if (cmd->FindStringRemain("/?",line)) { 00457 WriteOut(MSG_Get("SHELL_CMD_COMMAND_HELP")); 00458 return; 00459 } 00460 00461 if (this == first_shell) { 00462 /* Start a normal shell and check for a first command init */ 00463 WriteOut(MSG_Get("SHELL_STARTUP_BEGIN"),VERSION,SDL_STRING,UPDATED_STR); 00464 WriteOut(MSG_Get("SHELL_STARTUP_BEGIN2")); 00465 WriteOut(MSG_Get("SHELL_STARTUP_BEGIN3")); 00466 #if C_DEBUG 00467 WriteOut(MSG_Get("SHELL_STARTUP_DEBUG")); 00468 #endif 00469 if (machine == MCH_CGA || machine == MCH_AMSTRAD) WriteOut(MSG_Get("SHELL_STARTUP_CGA")); 00470 if (machine == MCH_PC98) WriteOut(MSG_Get("SHELL_STARTUP_PC98")); 00471 if (machine == MCH_HERC || machine == MCH_MDA) WriteOut(MSG_Get("SHELL_STARTUP_HERC")); 00472 WriteOut(MSG_Get("SHELL_STARTUP_END")); 00473 if (!countryNo) { 00474 #if defined(WIN32) 00475 char buffer[128]; 00476 if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ICOUNTRY, buffer, 128)) { 00477 countryNo = Bit16u(atoi(buffer)); 00478 DOS_SetCountry(countryNo); 00479 } 00480 else 00481 countryNo = 1; // Defaults to 1 (US) if failed 00482 #endif 00483 } 00484 strcpy(config_data, ""); 00485 Section_prop *section = static_cast<Section_prop *>(control->GetSection("config")); 00486 if (section!=NULL&&!control->opt_noconfig&&!control->opt_securemode&&!control->SecureMode()) { 00487 int country = section->Get_int("country"); 00488 if (country>0) { 00489 countryNo = country; 00490 DOS_SetCountry(countryNo); 00491 } 00492 const char * extra = const_cast<char*>(section->data.c_str()); 00493 if (extra) { 00494 std::istringstream in(extra); 00495 char linestr[CROSS_LEN+1], cmdstr[CROSS_LEN], valstr[CROSS_LEN], tmpstr[CROSS_LEN]; 00496 char *cmd=cmdstr, *val=valstr, *tmp=tmpstr, *p; 00497 if (in) for (std::string line; std::getline(in, line); ) { 00498 if (line.length()>CROSS_LEN) { 00499 strncpy(linestr, line.c_str(), CROSS_LEN); 00500 linestr[CROSS_LEN]=0; 00501 } else 00502 strcpy(linestr, line.c_str()); 00503 p=strchr(linestr, '='); 00504 if (p!=NULL) { 00505 *p=0; 00506 strcpy(cmd, linestr); 00507 strcpy(val, p+1); 00508 cmd=trim(cmd); 00509 val=trim(val); 00510 if (strlen(config_data)+strlen(cmd)+strlen(val)+3<CONFIG_SIZE) { 00511 strcat(config_data, cmd); 00512 strcat(config_data, "="); 00513 strcat(config_data, val); 00514 strcat(config_data, "\r\n"); 00515 } 00516 if (!strncasecmp(cmd, "set ", 4)) 00517 DoCommand((char *)(std::string(cmd)+"="+std::string(val)).c_str()); 00518 else if (!strcasecmp(cmd, "install")||!strcasecmp(cmd, "installhigh")||!strcasecmp(cmd, "device")||!strcasecmp(cmd, "devicehigh")) { 00519 strcpy(tmp, val); 00520 char *name=StripArg(tmp); 00521 if (!*name) continue; 00522 if (!DOS_FileExists(name)) { 00523 WriteOut("The following file is missing or corrupted: %s\n", name); 00524 continue; 00525 } 00526 if (!strcasecmp(cmd, "install")) 00527 DoCommand(val); 00528 else if (!strcasecmp(cmd, "installhigh")) 00529 DoCommand((char *)("lh "+std::string(val)).c_str()); 00530 else if (!strcasecmp(cmd, "device")) 00531 DoCommand((char *)("device "+std::string(val)).c_str()); 00532 else if (!strcasecmp(cmd, "devicehigh")) 00533 DoCommand((char *)("lh device "+std::string(val)).c_str()); 00534 } 00535 } else if (!strncasecmp(line.c_str(), "rem ", 4)) { 00536 strcat(config_data, line.c_str()); 00537 strcat(config_data, "\r\n"); 00538 } 00539 } 00540 } 00541 } 00542 if (!strlen(config_data)) { 00543 strcat(config_data, "rem="); 00544 strcat(config_data, (char *)section->Get_string("rem")); 00545 strcat(config_data, "\r\n"); 00546 } 00547 VFILE_Register("CONFIG.SYS",(Bit8u *)config_data,(Bit32u)strlen(config_data)); 00548 #if defined(WIN32) 00549 if (!control->opt_securemode&&!control->SecureMode()) 00550 { 00551 const Section_prop* sec = 0; sec = static_cast<Section_prop*>(control->GetSection("dos")); 00552 if(sec->Get_bool("automountall")) { 00553 Bit32u drives = GetLogicalDrives(); 00554 char name[4]="A:\\"; 00555 for (int i=0; i<25; i++) { 00556 if ((drives & (1<<i)) && !Drives[i]) 00557 { 00558 name[0]='A'+i; 00559 int type=GetDriveType(name); 00560 if (type!=DRIVE_NO_ROOT_DIR) { 00561 WriteOut("Mounting %c: => %s..\n", name[0], name); 00562 char mountstring[DOS_PATHLENGTH+CROSS_LEN+20]; 00563 sprintf(mountstring,"MOUNT %c ",name[0]); 00564 if(type==DRIVE_CDROM) strcat(mountstring,"-t cdrom "); 00565 else if(type==DRIVE_REMOVABLE && (strcasecmp(name,"A:\\")==0||strcasecmp(name,"B:\\")==0)) strcat(mountstring,"-t floppy "); 00566 strcat(mountstring,name); 00567 strcat(mountstring," >nul"); 00568 ParseLine(mountstring); 00569 if (!Drives[i]) WriteOut("Drive %c: failed to mount.\n",name[0]); 00570 else if(type==DRIVE_FIXED && (strcasecmp(name,"C:\\")==0)) WriteOut("Warning: %s", MSG_Get("SHELL_EXECUTE_DRIVE_ACCESS_WARNING_WIN")); 00571 } 00572 } 00573 } 00574 } 00575 } 00576 #endif 00577 00578 } 00579 else { 00580 WriteOut(optK?"\n":"DOSBox-X command shell [Version %s %s]\nCopyright DOSBox-X Team. All rights reserved\n\n",VERSION,SDL_STRING); 00581 } 00582 00583 if (cmd->FindString("/INIT",line,true)) { 00584 input_line[CMD_MAXLINE-1u] = 0; 00585 strncpy(input_line,line.c_str(),CMD_MAXLINE-1u); 00586 line.erase(); 00587 ParseLine(input_line); 00588 } 00589 do { 00590 /* Get command once a line */ 00591 if (bf) { 00592 if (bf->ReadLine(input_line)) { 00593 if (echo) { 00594 if (input_line[0]!='@') { 00595 ShowPrompt(); 00596 WriteOut_NoParsing(input_line); 00597 WriteOut_NoParsing("\n"); 00598 } 00599 } 00600 } else input_line[0]='\0'; 00601 } else { 00602 if (echo) ShowPrompt(); 00603 InputCommand(input_line); 00604 if (echo && !input_eof) WriteOut("\n"); 00605 00606 /* Bugfix: CTTY NUL will return immediately, the shell input will return 00607 * immediately, and if we don't consume CPU cycles to compensate, 00608 * will leave DOSBox-X running in an endless loop, hung. */ 00609 if (input_eof) CALLBACK_Idle(); 00610 } 00611 00612 /* do it */ 00613 if(strlen(input_line)!=0) { 00614 ParseLine(input_line); 00615 if (echo && !bf) WriteOut_NoParsing("\n"); 00616 } 00617 } while (!exit); 00618 } 00619 00620 void DOS_Shell::SyntaxError(void) { 00621 WriteOut(MSG_Get("SHELL_SYNTAXERROR")); 00622 } 00623 00624 class AUTOEXEC:public Module_base { 00625 private: 00626 AutoexecObject autoexec[17]; 00627 AutoexecObject autoexec_echo; 00628 AutoexecObject autoexec_auto_bat; 00629 public: 00630 AUTOEXEC(Section* configuration):Module_base(configuration) { 00631 /* Register a virtual AUTOEXEC.BAT file */ 00632 const Section_line * section=static_cast<Section_line *>(configuration); 00633 00634 /* Check -securemode switch to disable mount/imgmount/boot after running autoexec.bat */ 00635 bool secure = control->opt_securemode; 00636 00637 /* The user may have given .BAT files to run on the command line */ 00638 if (!control->auto_bat_additional.empty()) { 00639 std::string cmd; 00640 00641 for (unsigned int i=0;i<control->auto_bat_additional.size();i++) { 00642 std::string batname; 00643 /* NTS: this code might have problems with DBCS filenames - yksoft1 */ 00644 00645 std::replace(control->auto_bat_additional[i].begin(),control->auto_bat_additional[i].end(),'/','\\'); 00646 size_t pos = control->auto_bat_additional[i].find_last_of('\\'); 00647 if(pos == std::string::npos) { //Only a filename, mount current directory 00648 batname = control->auto_bat_additional[i]; 00649 cmd += "@mount c: . -q\n"; 00650 } else { //Parse the path of .BAT file 00651 std::string batpath = control->auto_bat_additional[i].substr(0,pos+1); 00652 if (batpath==".\\") batpath="."; 00653 else if (batpath=="..\\") batpath=".."; 00654 batname = control->auto_bat_additional[i].substr(pos+1); 00655 cmd += "@mount c: \"" + batpath + "\" -q\n"; 00656 } 00657 cmd += "@c:\n"; 00658 cmd += "@cd \\\n"; 00659 cmd += "@CALL \""; 00660 cmd += batname; 00661 cmd += "\"\n"; 00662 cmd += "@mount -u c: -q\n"; 00663 } 00664 00665 autoexec_auto_bat.Install(cmd); 00666 } 00667 00668 /* add stuff from the configfile unless -noautexec or -securemode is specified. */ 00669 const char * extra = const_cast<char*>(section->data.c_str()); 00670 if (extra && !secure && !control->opt_noautoexec) { 00671 /* detect if "echo off" is the first line */ 00672 size_t firstline_length = strcspn(extra,"\r\n"); 00673 bool echo_off = !strncasecmp(extra,"echo off",8); 00674 if (echo_off && firstline_length == 8) extra += 8; 00675 else { 00676 echo_off = !strncasecmp(extra,"@echo off",9); 00677 if (echo_off && firstline_length == 9) extra += 9; 00678 else echo_off = false; 00679 } 00680 00681 /* if "echo off" move it to the front of autoexec.bat */ 00682 if (echo_off) { 00683 autoexec_echo.InstallBefore("@echo off"); 00684 if (*extra == '\r') extra++; //It can point to \0 00685 if (*extra == '\n') extra++; //same 00686 } 00687 00688 /* Install the stuff from the configfile if anything left after moving echo off */ 00689 00690 if (*extra) autoexec[0].Install(std::string(extra)); 00691 } 00692 00693 /* Check to see for extra command line options to be added (before the command specified on commandline) */ 00694 /* Maximum of extra commands: 10 */ 00695 Bitu i = 1; 00696 for (auto it=control->opt_c.begin();i <= 11 && it!=control->opt_c.end();it++) /* -c switches */ 00697 autoexec[i++].Install(*it); 00698 00699 /* Check for the -exit switch which causes dosbox to when the command on the commandline has finished */ 00700 bool addexit = control->opt_exit; 00701 00702 #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. */ 00703 /* Check for first command being a directory or file */ 00704 char buffer[CROSS_LEN+1]; 00705 char orig[CROSS_LEN+1]; 00706 char cross_filesplit[2] = {CROSS_FILESPLIT , 0}; 00707 00708 Bitu dummy = 1; 00709 bool command_found = false; 00710 while (control->cmdline->FindCommand(dummy++,line) && !command_found) { 00711 struct stat test; 00712 if (line.length() > CROSS_LEN) continue; 00713 strcpy(buffer,line.c_str()); 00714 if (stat(buffer,&test)) { 00715 if (getcwd(buffer,CROSS_LEN) == NULL) continue; 00716 if (strlen(buffer) + line.length() + 1 > CROSS_LEN) continue; 00717 strcat(buffer,cross_filesplit); 00718 strcat(buffer,line.c_str()); 00719 if (stat(buffer,&test)) continue; 00720 } 00721 if (test.st_mode & S_IFDIR) { 00722 autoexec[12].Install(std::string("MOUNT C \"") + buffer + "\""); 00723 autoexec[13].Install("C:"); 00724 if(secure) autoexec[14].Install("z:\\config.com -securemode"); 00725 command_found = true; 00726 } else { 00727 char* name = strrchr(buffer,CROSS_FILESPLIT); 00728 if (!name) { //Only a filename 00729 line = buffer; 00730 if (getcwd(buffer,CROSS_LEN) == NULL) continue; 00731 if (strlen(buffer) + line.length() + 1 > CROSS_LEN) continue; 00732 strcat(buffer,cross_filesplit); 00733 strcat(buffer,line.c_str()); 00734 if(stat(buffer,&test)) continue; 00735 name = strrchr(buffer,CROSS_FILESPLIT); 00736 if(!name) continue; 00737 } 00738 *name++ = 0; 00739 if (access(buffer,F_OK)) continue; 00740 autoexec[12].Install(std::string("MOUNT C \"") + buffer + "\""); 00741 autoexec[13].Install("C:"); 00742 /* Save the non-modified filename (so boot and imgmount can use it (long filenames, case sensivitive)) */ 00743 strcpy(orig,name); 00744 upcase(name); 00745 if(strstr(name,".BAT") != 0) { 00746 if(secure) autoexec[14].Install("z:\\config.com -securemode"); 00747 /* BATch files are called else exit will not work */ 00748 autoexec[15].Install(std::string("CALL ") + name); 00749 if(addexit) autoexec[16].Install("exit"); 00750 } else if((strstr(name,".IMG") != 0) || (strstr(name,".IMA") !=0 )) { 00751 //No secure mode here as boot is destructive and enabling securemode disables boot 00752 /* Boot image files */ 00753 autoexec[15].Install(std::string("BOOT ") + orig); 00754 } else if((strstr(name,".ISO") != 0) || (strstr(name,".CUE") !=0 )) { 00755 /* imgmount CD image files */ 00756 /* securemode gets a different number from the previous branches! */ 00757 autoexec[14].Install(std::string("IMGMOUNT D \"") + orig + std::string("\" -t iso")); 00758 //autoexec[16].Install("D:"); 00759 if(secure) autoexec[15].Install("z:\\config.com -securemode"); 00760 /* Makes no sense to exit here */ 00761 } else { 00762 if(secure) autoexec[14].Install("z:\\config.com -securemode"); 00763 autoexec[15].Install(name); 00764 if(addexit) autoexec[16].Install("exit"); 00765 } 00766 command_found = true; 00767 } 00768 } 00769 00770 /* Combining -securemode, noautoexec and no parameters leaves you with a lovely Z:\. */ 00771 if ( !command_found ) { 00772 if ( secure ) autoexec[12].Install("z:\\config.com -securemode"); 00773 } 00774 #else 00775 if (secure) autoexec[i++].Install("z:\\config.com -securemode"); 00776 #endif 00777 00778 if (addexit) autoexec[i++].Install("exit"); 00779 00780 assert(i <= 17); /* FIXME: autoexec[] should not be fixed size */ 00781 00782 VFILE_Register("AUTOEXEC.BAT",(Bit8u *)autoexec_data,(Bit32u)strlen(autoexec_data)); 00783 } 00784 }; 00785 00786 static AUTOEXEC* test = NULL; 00787 00788 static void AUTOEXEC_ShutDown(Section * sec) { 00789 (void)sec;//UNUSED 00790 if (test != NULL) { 00791 delete test; 00792 test = NULL; 00793 } 00794 if (first_shell != NULL) { 00795 delete first_shell; 00796 first_shell = 0;//Make clear that it shouldn't be used anymore 00797 } 00798 if (call_shellstop != 0) { 00799 CALLBACK_DeAllocate(call_shellstop); 00800 call_shellstop = 0; 00801 } 00802 } 00803 00804 void AUTOEXEC_Startup(Section *sec) { 00805 (void)sec;//UNUSED 00806 if (test == NULL) { 00807 LOG(LOG_MISC,LOG_DEBUG)("Allocating AUTOEXEC.BAT emulation"); 00808 test = new AUTOEXEC(control->GetSection("autoexec")); 00809 } 00810 } 00811 00812 void AUTOEXEC_Init() { 00813 LOG(LOG_MISC,LOG_DEBUG)("Initializing AUTOEXEC.BAT emulation"); 00814 00815 AddExitFunction(AddExitFunctionFuncPair(AUTOEXEC_ShutDown)); 00816 AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(AUTOEXEC_ShutDown)); 00817 AddVMEventFunction(VM_EVENT_DOS_EXIT_BEGIN,AddVMEventFunctionFuncPair(AUTOEXEC_ShutDown)); 00818 AddVMEventFunction(VM_EVENT_DOS_EXIT_REBOOT_BEGIN,AddVMEventFunctionFuncPair(AUTOEXEC_ShutDown)); 00819 AddVMEventFunction(VM_EVENT_DOS_SURPRISE_REBOOT,AddVMEventFunctionFuncPair(AUTOEXEC_ShutDown)); 00820 } 00821 00822 static Bitu INT2E_Handler(void) { 00823 /* Save return address and current process */ 00824 RealPt save_ret=real_readd(SegValue(ss),reg_sp); 00825 Bit16u save_psp=dos.psp(); 00826 00827 /* Set first shell as process and copy command */ 00828 dos.psp(shell_psp);//DOS_FIRST_SHELL); 00829 DOS_PSP psp(shell_psp);//DOS_FIRST_SHELL); 00830 psp.SetCommandTail(RealMakeSeg(ds,reg_si)); 00831 SegSet16(ss,RealSeg(psp.GetStack())); 00832 reg_sp=2046; 00833 00834 /* Read and fix up command string */ 00835 CommandTail tail; 00836 MEM_BlockRead(PhysMake(dos.psp(),CTBUF+1),&tail,CTBUF+1); 00837 if (tail.count<CTBUF) tail.buffer[tail.count]=0; 00838 else tail.buffer[CTBUF-1]=0; 00839 char* crlf=strpbrk(tail.buffer,"\r\n"); 00840 if (crlf) *crlf=0; 00841 00842 /* Execute command */ 00843 if (strlen(tail.buffer)) { 00844 DOS_Shell temp; 00845 temp.ParseLine(tail.buffer); 00846 temp.RunInternal(); 00847 } 00848 00849 /* Restore process and "return" to caller */ 00850 dos.psp(save_psp); 00851 SegSet16(cs,RealSeg(save_ret)); 00852 reg_ip=RealOff(save_ret); 00853 reg_ax=0; 00854 return CBRET_NONE; 00855 } 00856 00857 static char const * const path_string="PATH=Z:\\"; 00858 static char const * const comspec_string="COMSPEC=Z:\\COMMAND.COM"; 00859 static char const * const prompt_string="PROMPT=$P$G"; 00860 static char const * const full_name="Z:\\COMMAND.COM"; 00861 static char const * const init_line="/INIT AUTOEXEC.BAT"; 00862 00863 extern unsigned int dosbox_shell_env_size; 00864 00865 /* TODO: Why is all this DOS kernel and VFILE registration here in SHELL_Init()? 00866 * That's like claiming that DOS memory and device initialization happens from COMMAND.COM! 00867 * We need to move the DOS kernel initialization into another function, and the VFILE 00868 * registration to another function, and then message initialization to another function, 00869 * and then those functions need to be called before SHELL_Init() -J.C. */ 00870 void SHELL_Init() { 00871 LOG(LOG_MISC,LOG_DEBUG)("Initializing DOS shell"); 00872 00873 /* Add messages */ 00874 MSG_Add("SHELL_CMD_VOL_DRIVE","\n Volume in drive %c "); 00875 MSG_Add("SHELL_CMD_VOL_DRIVEERROR","Cannot find the drive specified\n"); 00876 MSG_Add("SHELL_CMD_VOL_SERIAL"," Volume Serial Number is "); 00877 MSG_Add("SHELL_CMD_VOL_SERIAL_NOLABEL","has no label\n"); 00878 MSG_Add("SHELL_CMD_VOL_SERIAL_LABEL","is %s\n"); 00879 MSG_Add("SHELL_ILLEGAL_PATH","Illegal Path.\n"); 00880 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"); 00881 MSG_Add("SHELL_CMD_ECHO_ON","ECHO is on.\n"); 00882 MSG_Add("SHELL_CMD_ECHO_OFF","ECHO is off.\n"); 00883 MSG_Add("SHELL_ILLEGAL_CONTROL_CHARACTER","Unexpected control character: Dec %03u and Hex %#04x.\n"); 00884 MSG_Add("SHELL_ILLEGAL_SWITCH","Illegal switch: %s.\n"); 00885 MSG_Add("SHELL_MISSING_PARAMETER","Required parameter missing.\n"); 00886 MSG_Add("SHELL_CMD_CHDIR_ERROR","Unable to change to: %s.\n"); 00887 MSG_Add("SHELL_CMD_CHDIR_HINT","Hint: To change to different drive type \033[31m%c:\033[0m\n"); 00888 MSG_Add("SHELL_CMD_CHDIR_HINT_2","directoryname contains unquoted spaces.\nTry \033[31mcd %s\033[0m or properly quote them with quotation marks.\n"); 00889 MSG_Add("SHELL_CMD_CHDIR_HINT_3","You are still on drive Z:, change to a mounted drive with \033[31mC:\033[0m.\n"); 00890 MSG_Add("SHELL_CMD_DATE_HELP","Displays or changes the internal date.\n"); 00891 MSG_Add("SHELL_CMD_DATE_ERROR","The specified date is not correct.\n"); 00892 MSG_Add("SHELL_CMD_DATE_DAYS","3SunMonTueWedThuFriSat"); // "2SoMoDiMiDoFrSa" 00893 MSG_Add("SHELL_CMD_DATE_NOW","Current date: "); 00894 MSG_Add("SHELL_CMD_DATE_SETHLP","Type 'date %s' to change.\n"); 00895 MSG_Add("SHELL_CMD_DATE_HELP_LONG","DATE [[/T] [/H] [/S] | date]\n"\ 00896 " date: New date to set\n"\ 00897 " /S: Permanently use host time and date as DOS time\n"\ 00898 " /F: Switch back to DOSBox-X internal time (opposite of /S)\n"\ 00899 " /T: Only display date\n"\ 00900 " /H: Synchronize with host\n"); 00901 MSG_Add("SHELL_CMD_TIME_HELP","Displays or changes the internal time.\n"); 00902 MSG_Add("SHELL_CMD_TIME_ERROR","The specified time is not correct.\n"); 00903 MSG_Add("SHELL_CMD_TIME_NOW","Current time: "); 00904 MSG_Add("SHELL_CMD_TIME_SETHLP","Type 'time %s' to change.\n"); 00905 MSG_Add("SHELL_CMD_TIME_HELP_LONG","TIME [[/T] [/H] | time]\n"\ 00906 " time: New time to set\n"\ 00907 " /T: Display simple time\n"\ 00908 " /H: Synchronize with host\n"); 00909 MSG_Add("SHELL_CMD_MKDIR_ERROR","Unable to make: %s.\n"); 00910 MSG_Add("SHELL_CMD_RMDIR_ERROR","Unable to remove: %s.\n"); 00911 MSG_Add("SHELL_CMD_RENAME_ERROR","Unable to rename: %s.\n"); 00912 MSG_Add("SHELL_CMD_ATTRIB_GET_ERROR","Unable to get attributes: %s\n"); 00913 MSG_Add("SHELL_CMD_ATTRIB_SET_ERROR","Unable to set attributes: %s\n"); 00914 MSG_Add("SHELL_CMD_DEL_ERROR","Unable to delete: %s.\n"); 00915 MSG_Add("SHELL_CMD_DEL_SURE","All files in directory will be deleted!\nAre you sure [Y/N]?"); 00916 MSG_Add("SHELL_SYNTAXERROR","The syntax of the command is incorrect.\n"); 00917 MSG_Add("SHELL_CMD_SET_NOT_SET","Environment variable %s not defined.\n"); 00918 MSG_Add("SHELL_CMD_SET_OUT_OF_SPACE","Not enough environment space left.\n"); 00919 MSG_Add("SHELL_CMD_IF_EXIST_MISSING_FILENAME","IF EXIST: Missing filename.\n"); 00920 MSG_Add("SHELL_CMD_IF_ERRORLEVEL_MISSING_NUMBER","IF ERRORLEVEL: Missing number.\n"); 00921 MSG_Add("SHELL_CMD_IF_ERRORLEVEL_INVALID_NUMBER","IF ERRORLEVEL: Invalid number.\n"); 00922 MSG_Add("SHELL_CMD_GOTO_MISSING_LABEL","No label supplied to GOTO command.\n"); 00923 MSG_Add("SHELL_CMD_GOTO_LABEL_NOT_FOUND","GOTO: Label %s not found.\n"); 00924 MSG_Add("SHELL_CMD_FILE_NOT_FOUND","File %s not found.\n"); 00925 MSG_Add("SHELL_CMD_FILE_EXISTS","File %s already exists.\n"); 00926 MSG_Add("SHELL_CMD_DIR_INTRO"," Directory of %s\n\n"); 00927 MSG_Add("SHELL_CMD_DIR_BYTES_USED","%5d File(s) %17s Bytes\n"); 00928 MSG_Add("SHELL_CMD_DIR_BYTES_FREE","%5d Dir(s) %17s Bytes free\n"); 00929 MSG_Add("SHELL_CMD_DIR_FILES_LISTED","Total files listed:\n"); 00930 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"); 00931 MSG_Add("SHELL_EXECUTE_DRIVE_ACCESS_CDROM","Do you want to give DOSBox-X access to your real CD-ROM drive %c [Y/N]?"); 00932 MSG_Add("SHELL_EXECUTE_DRIVE_ACCESS_FLOPPY","Do you want to give DOSBox-X access to your real floppy drive %c [Y/N]?"); 00933 MSG_Add("SHELL_EXECUTE_DRIVE_ACCESS_REMOVABLE","Do you want to give DOSBox-X access to your real removable drive %c [Y/N]?"); 00934 MSG_Add("SHELL_EXECUTE_DRIVE_ACCESS_NETWORK","Do you want to give DOSBox-X access to your real network drive %c [Y/N]?"); 00935 MSG_Add("SHELL_EXECUTE_DRIVE_ACCESS_FIXED","Do you really want to give DOSBox-X access to everything\non your real drive %c [Y/N]?"); 00936 MSG_Add("SHELL_EXECUTE_DRIVE_ACCESS_WARNING_WIN","Mounting C:\\ is NOT recommended.\n"); 00937 MSG_Add("SHELL_EXECUTE_ILLEGAL_COMMAND","Illegal command: %s.\n"); 00938 MSG_Add("SHELL_CMD_PAUSE","Press any key to continue.\n"); 00939 MSG_Add("SHELL_CMD_PAUSE_HELP","Waits for one keystroke to continue.\n"); 00940 MSG_Add("SHELL_CMD_PAUSE_HELP_LONG","PAUSE\n"); 00941 MSG_Add("SHELL_CMD_COPY_FAILURE","Copy failure : %s.\n"); 00942 MSG_Add("SHELL_CMD_COPY_SUCCESS"," %d File(s) copied.\n"); 00943 MSG_Add("SHELL_CMD_COPY_CONFIRM","Overwrite %s (Yes/No/All)?"); 00944 MSG_Add("SHELL_CMD_COPY_NOSPACE","Insufficient disk space - %s\n"); 00945 MSG_Add("SHELL_CMD_COPY_ERROR","Error in copying file %s\n"); 00946 MSG_Add("SHELL_CMD_SUBST_DRIVE_LIST","The currently mounted local drives are:\n"); 00947 MSG_Add("SHELL_CMD_SUBST_NO_REMOVE","Unable to remove, drive not in use.\n"); 00948 MSG_Add("SHELL_CMD_SUBST_IN_USE","Target drive is already in use.\n"); 00949 MSG_Add("SHELL_CMD_SUBST_NOT_LOCAL","It is only possible to use SUBST on local drives.\n"); 00950 MSG_Add("SHELL_CMD_SUBST_INVALID_PATH","The specified drive or path is invalid.\n"); 00951 MSG_Add("SHELL_CMD_SUBST_FAILURE","SUBST: There is an error in your command line.\n"); 00952 00953 std::string mapper_keybind = mapper_event_keybind_string("host"); 00954 if (mapper_keybind.empty()) mapper_keybind = "unbound"; 00955 00956 /* Capitalize the binding */ 00957 if (mapper_keybind.size() > 0) 00958 mapper_keybind[0] = toupper(mapper_keybind[0]); 00959 00960 /* Punctuation is important too. */ 00961 mapper_keybind += "."; 00962 00963 /* NTS: MSG_Add() takes the string as const char * but it does make a copy of the string when entering into the message map, 00964 * so there is no problem here of causing use-after-free crashes when we exit. */ 00965 std::string host_key_help; // SHELL_STARTUP_BEGIN2 00966 00967 if (machine == MCH_PC98) { 00968 // "\x86\x46 To activate the keymapper \033[31mhost+M\033[37m. Host key is F12. \x86\x46\n" 00969 host_key_help = 00970 std::string("\x86\x46 To activate the keymapper \033[31mhost+M\033[37m. Host key is ") + 00971 (mapper_keybind + " ").substr(0,20) + 00972 std::string(" \x86\x46\n"); 00973 } 00974 else { 00975 // "\xBA To activate the keymapper \033[31mhost+M\033[37m. Host key is F12. \xBA\n" 00976 host_key_help = 00977 std::string("\033[44;1m\xBA To activate the keymapper \033[31mhost+M\033[37m. Host key is ") + 00978 (mapper_keybind + " ").substr(0,20) + 00979 std::string(" \xBA\033[0m\n"); 00980 } 00981 00982 if (machine == MCH_PC98) { 00983 MSG_Add("SHELL_STARTUP_BEGIN", 00984 "\x86\x52\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 00985 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 00986 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 00987 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 00988 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 00989 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x56\n" 00990 "\x86\x46 \033[32mWelcome to DOSBox-X %-8s (%-4s) %-25s\033[37m \x86\x46\n" 00991 "\x86\x46 \x86\x46\n" 00992 "\x86\x46 For a short introduction for new users type: \033[33mINTRO\033[37m \x86\x46\n" 00993 "\x86\x46 For supported shell commands type: \033[33mHELP\033[37m \x86\x46\n" 00994 "\x86\x46 \x86\x46\n" 00995 "\x86\x46 To adjust the emulated CPU speed, use \033[31mhost -\033[37m and \033[31mhost +\033[37m. \x86\x46\n"); 00996 MSG_Replace("SHELL_STARTUP_BEGIN2", 00997 host_key_help.c_str()); 00998 MSG_Add("SHELL_STARTUP_BEGIN3", 00999 "\x86\x46 For more information read the online guide in the \033[36mDOSBox-X Wiki\033[37m. \x86\x46\n" 01000 "\x86\x46 \x86\x46\n" 01001 ); 01002 MSG_Add("SHELL_STARTUP_PC98","\x86\x46 DOSBox-X is now running in NEC PC-98 emulation mode. \x86\x46\n" 01003 "\x86\x46 \033[31mPC-98 emulation is INCOMPLETE and CURRENTLY IN DEVELOPMENT.\033[37m \x86\x46\n"); 01004 MSG_Add("SHELL_STARTUP_DEBUG", 01005 #if defined(MACOSX) 01006 "\x86\x46 Debugger is available, use \033[31mAlt-F12\033[37m to enter. \x86\x46\n" 01007 #else 01008 "\x86\x46 Debugger is available, use \033[31mAlt-Pause\033[37m to enter. \x86\x46\n" 01009 #endif 01010 "\x86\x46 \x86\x46\n" 01011 ); 01012 MSG_Add("SHELL_STARTUP_END", 01013 "\x86\x46 \033[32mDOSBox-X project \033[33mhttp://dosbox-x.software\033[32m PentiumPro support \033[37m \x86\x46\n" 01014 "\x86\x46 \033[32mDerived from DOSBox \033[33mhttp://www.dosbox.com\033[37m \x86\x46\n" 01015 "\x86\x5A\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 01016 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 01017 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 01018 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 01019 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44" 01020 "\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x44\x86\x5E\033[0m\n" 01021 "\033[1m\033[32mHAVE FUN!\033[0m\n" 01022 ); 01023 } 01024 else { 01025 MSG_Add("SHELL_STARTUP_BEGIN", 01026 "\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" 01027 "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" 01028 "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBB\033[0m\n" 01029 "\033[44;1m\xBA \033[32mWelcome to DOSBox-X %-8s (%-4s) %-25s\033[37m \xBA\033[0m\n" 01030 "\033[44;1m\xBA \xBA\033[0m\n" 01031 "\033[44;1m\xBA For a short introduction for new users type: \033[33mINTRO\033[37m \xBA\033[0m\n" 01032 "\033[44;1m\xBA For supported shell commands type: \033[33mHELP\033[37m \xBA\033[0m\n" 01033 "\033[44;1m\xBA \xBA\033[0m\n" 01034 "\033[44;1m\xBA To adjust the emulated CPU speed, use \033[31mhost -\033[37m and \033[31mhost +\033[37m. \xBA\033[0m\n" 01035 ); 01036 MSG_Replace("SHELL_STARTUP_BEGIN2", 01037 host_key_help.c_str()); 01038 MSG_Add("SHELL_STARTUP_BEGIN3", 01039 "\033[44;1m\xBA For more information read the online guide in the \033[36mDOSBox-X Wiki\033[37m. \xBA\033[0m\n" 01040 "\033[44;1m\xBA \xBA\033[0m\n" 01041 ); 01042 if (!mono_cga) { 01043 MSG_Add("SHELL_STARTUP_CGA","\033[44;1m\xBA DOSBox-X supports Composite CGA mode. \xBA\033[0m\n" 01044 "\033[44;1m\xBA Use \033[31mF12\033[37m to set composite output ON, OFF, or AUTO (default). \xBA\033[0m\n" 01045 "\033[44;1m\xBA \033[31m(Alt-)F11\033[37m changes hue; \033[31mctrl-alt-F11\033[37m selects early/late CGA model. \xBA\033[0m\n" 01046 "\033[44;1m\xBA \xBA\033[0m\n" 01047 ); 01048 } else { 01049 MSG_Add("SHELL_STARTUP_CGA","\033[44;1m\xBA Use \033[31mF11\033[37m to cycle through green, amber, and white monochrome color, \xBA\033[0m\n" 01050 "\033[44;1m\xBA and \033[31mAlt-F11\033[37m to change contrast/brightness settings. \xBA\033[0m\n" 01051 ); 01052 } 01053 MSG_Add("SHELL_STARTUP_PC98","\xBA DOSBox-X is now running in NEC PC-98 emulation mode. \xBA\n" 01054 "\xBA \033[31mPC-98 emulation is INCOMPLETE and CURRENTLY IN DEVELOPMENT.\033[37m \xBA\n"); 01055 MSG_Add("SHELL_STARTUP_HERC","\033[44;1m\xBA Use F11 to cycle through white, amber, and green monochrome color. \xBA\033[0m\n" 01056 "\033[44;1m\xBA Use alt-F11 to toggle horizontal blending (only in graphics mode). \xBA\033[0m\n" 01057 "\033[44;1m\xBA \xBA\033[0m\n" 01058 ); 01059 MSG_Add("SHELL_STARTUP_DEBUG", 01060 #if defined(MACOSX) 01061 "\033[44;1m\xBA Debugger is available, use \033[31mAlt-F12\033[37m to enter. \xBA\033[0m\n" 01062 #else 01063 "\033[44;1m\xBA Debugger is available, use \033[31mAlt-Pause\033[37m to enter. \xBA\033[0m\n" 01064 #endif 01065 "\033[44;1m\xBA \xBA\033[0m\n" 01066 ); 01067 MSG_Add("SHELL_STARTUP_END", 01068 "\033[44;1m\xBA \033[32mDOSBox-X project \033[33mhttp://dosbox-x.software\033[32m PentiumPro support \033[37m \xBA\033[0m\n" 01069 "\033[44;1m\xBA \033[32mDerived from DOSBox \033[33mhttp://www.dosbox.com\033[37m \xBA\033[0m\n" 01070 "\033[44;1m\xC8\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" 01071 "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" 01072 "\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" 01073 "\033[1m\033[32mHAVE FUN!\033[0m\n" 01074 ); 01075 } 01076 01077 MSG_Add("SHELL_CMD_BREAK_HELP","Sets or clears extended CTRL+C checking.\n"); 01078 MSG_Add("SHELL_CMD_BREAK_HELP_LONG","BREAK [ON | OFF]\n\nType BREAK without a parameter to display the current BREAK setting.\n"); 01079 MSG_Add("SHELL_CMD_CHDIR_HELP","Displays or changes the current directory.\n"); 01080 MSG_Add("SHELL_CMD_CHDIR_HELP_LONG","CHDIR [drive:][path]\n" 01081 "CHDIR [..]\n" 01082 "CD [drive:][path]\n" 01083 "CD [..]\n\n" 01084 " .. Specifies that you want to change to the parent directory.\n\n" 01085 "Type CD drive: to display the current directory in the specified drive.\n" 01086 "Type CD without parameters to display the current drive and directory.\n"); 01087 MSG_Add("SHELL_CMD_CLS_HELP","Clears screen.\n"); 01088 MSG_Add("SHELL_CMD_CLS_HELP_LONG","CLS\n"); 01089 MSG_Add("SHELL_CMD_DIR_HELP","Displays a list of files and subdirectories in a directory.\n"); 01090 MSG_Add("SHELL_CMD_DIR_HELP_LONG","DIR [drive:][path][filename] [/[W|B]] [/S] [/P] [/A[D|H|S|R|A]] [/O[N|E|G|S|D]]\n\n" 01091 " [drive:][path][filename]\n" 01092 " Specifies drive, directory, and/or files to list.\n" 01093 " /W Uses wide list format.\n" 01094 " /B Uses bare format (no heading information or summary).\n" 01095 " /S Displays files in specified directory and all subdirectories.\n" 01096 " /P Pauses after each screenful of information.\n" 01097 " /A Displays files with specified attributes.\n" 01098 " attributes D Directories R Read-only files\n" 01099 " H Hidden files A Files ready for archiving\n" 01100 " S System files - Prefix meaning not\n" 01101 " /O List by files in sorted order.\n" 01102 " sortorder N By name (alphabetic) S By size (smallest first)\n" 01103 " E By extension (alphabetic) D By date & time (earlist first)\n" 01104 " G Group directories first - Prefix to reverse order\n\n" 01105 "Switches may be preset in the DIRCMD environment variable. Override\n" 01106 "preset switches by prefixing any switch with - (hyphen)--for example, /-W.\n" 01107 ); 01108 MSG_Add("SHELL_CMD_ECHO_HELP","Displays messages, or turns command-echoing on or off.\n"); 01109 MSG_Add("SHELL_CMD_ECHO_HELP_LONG"," ECHO [ON | OFF]\n ECHO [message]\n\nType ECHO without parameters to display the current echo setting.\n"); 01110 MSG_Add("SHELL_CMD_EXIT_HELP","Exits from the command shell.\n"); 01111 MSG_Add("SHELL_CMD_EXIT_HELP_LONG","EXIT\n"); 01112 MSG_Add("SHELL_CMD_HELP_HELP","Shows DOSBox-X command help.\n"); 01113 MSG_Add("SHELL_CMD_HELP_HELP_LONG","HELP [/A or /ALL]\nHELP [command]\n\n" 01114 " /A or /ALL\tLists all supported internal commands.\n\n" 01115 "Note: HELP will not list external commands such as MOUNT and IMGMOUNT.\n"); 01116 MSG_Add("SHELL_CMD_MKDIR_HELP","Creates a directory.\n"); 01117 MSG_Add("SHELL_CMD_MKDIR_HELP_LONG","MKDIR [drive:][path]\n" 01118 "MD [drive:][path]\n"); 01119 MSG_Add("SHELL_CMD_RMDIR_HELP","Removes a directory.\n"); 01120 MSG_Add("SHELL_CMD_RMDIR_HELP_LONG","RMDIR [drive:][path]\n" 01121 "RD [drive:][path]\n"); 01122 MSG_Add("SHELL_CMD_SET_HELP","Displays or changes environment variables.\n"); 01123 MSG_Add("SHELL_CMD_SET_HELP_LONG","SET [variable=[string]]\n\n" 01124 " variable\tSpecifies the environment-variable name.\n" 01125 " string\tSpecifies a series of characters to assign to the variable.\n\n" 01126 "* If no string is specified, the variable is removed from the environment.\n\n" 01127 "Type SET without parameters to display the current environment variables.\n"); 01128 MSG_Add("SHELL_CMD_IF_HELP","Performs conditional processing in batch programs.\n"); 01129 MSG_Add("SHELL_CMD_IF_HELP_LONG","IF [NOT] ERRORLEVEL number command\n" 01130 "IF [NOT] string1==string2 command\n" 01131 "IF [NOT] EXIST filename command\n\n" 01132 " NOT Specifies that DOS should carry out\n" 01133 " the command only if the condition is false.\n\n" 01134 " ERRORLEVEL number Specifies a true condition if the last program run\n" 01135 " returned an exit code equal to or greater than the number\n" 01136 " specified.\n\n" 01137 " string1==string2 Specifies a true condition if the specified text strings\n" 01138 " match.\n\n" 01139 " EXIST filename Specifies a true condition if the specified filename\n" 01140 " exists.\n\n" 01141 " command Specifies the command to carry out if the condition is\n" 01142 " met. Command can be followed by ELSE command which\n" 01143 " will execute the command after the ELSE keyword if the\n" 01144 " specified condition is FALSE\n"); 01145 MSG_Add("SHELL_CMD_GOTO_HELP","Jumps to a labeled line in a batch program.\n"); 01146 MSG_Add("SHELL_CMD_GOTO_HELP_LONG","GOTO label\n\n" 01147 " label Specifies a text string used in the batch program as a label.\n\n" 01148 "You type a label on a line by itself, beginning with a colon.\n"); 01149 MSG_Add("SHELL_CMD_SHIFT_HELP","Changes the position of replaceable parameters in a batch file.\n"); 01150 MSG_Add("SHELL_CMD_SHIFT_HELP_LONG","SHIFT\n"); 01151 MSG_Add("SHELL_CMD_FOR_HELP","Runs a specified command for each file in a set of files.\n"); 01152 MSG_Add("SHELL_CMD_FOR_HELP_LONG","FOR %%variable IN (set) DO command [command-parameters]\n\n %%variable Specifies a replaceable parameter.\n (set) Specifies a set of one or more files. Wildcards may be used.\n command Specifies the command to carry out for each file.\n command-parameters\n Specifies parameters or switches for the specified command.\n\nTo use the command in a batch program, specify %%%%variable instead of %%variable.\n"); 01153 MSG_Add("SHELL_CMD_LFNFOR_HELP","Enables or disables long filenames when processing FOR wildcards.\n"); 01154 MSG_Add("SHELL_CMD_LFNFOR_HELP_LONG","LFNFOR [ON | OFF]\n\nType LFNFOR without a parameter to display the current LFNFOR setting.\n\nThis command is only useful if LFN support is currently enabled.\n"); 01155 MSG_Add("SHELL_CMD_TYPE_HELP","Displays the contents of a text-file.\n"); 01156 MSG_Add("SHELL_CMD_TYPE_HELP_LONG","TYPE [drive:][path][filename]\n"); 01157 MSG_Add("SHELL_CMD_REM_HELP","Adds comments in a batch file.\n"); 01158 MSG_Add("SHELL_CMD_REM_HELP_LONG","REM [comment]\n"); 01159 MSG_Add("SHELL_CMD_RENAME_HELP","Renames a file/directory or files.\n"); 01160 MSG_Add("SHELL_CMD_RENAME_HELP_LONG","RENAME [drive:][path][directoryname1 | filename1] [directoryname2 | filename2]\n" 01161 "REN [drive:][path][directoryname1 | filename1] [directoryname2 | filename2]\n\n" 01162 "Note that you can not specify a new drive or path for your destination.\n"); 01163 MSG_Add("SHELL_CMD_DELETE_HELP","Removes one or more files.\n"); 01164 MSG_Add("SHELL_CMD_DELETE_HELP_LONG","DEL [/P] [/F] [/Q] names\n" 01165 "ERASE [/P] [/F] [/Q] names\n\n" 01166 " names\t\tSpecifies a list of one or more files or directories.\n" 01167 "\t\tWildcards may be used to delete multiple files. If a\n" 01168 "\t\tdirectory is specified, all files within the directory\n" 01169 "\t\twill be deleted.\n" 01170 " /P\t\tPrompts for confirmation before deleting one or more files.\n" 01171 " /F\t\tForce deleting of read-only files.\n" 01172 " /Q\t\tQuiet mode, do not ask if ok to delete on global wildcard\n"); 01173 MSG_Add("SHELL_CMD_COPY_HELP","Copies one or more files.\n"); 01174 MSG_Add("SHELL_CMD_COPY_HELP_LONG","COPY [/Y | /-Y] source [+source [+ ...]] [destination]\n\n" 01175 " source\tSpecifies the file or files to be copied.\n" 01176 " destination\tSpecifies the directory and/or filename for the new file(s).\n" 01177 " /Y\t\tSuppresses prompting to confirm you want to overwrite an\n\t\texisting destination file.\n" 01178 " /-Y\t\tCauses prompting to confirm you want to overwrite an\n\t\texisting destination file.\n\n" 01179 "The switch /Y may be preset in the COPYCMD environment variable.\n" 01180 "This may be overridden with /-Y on the command line.\n\n" 01181 "To append files, specify a single file for destination, but multiple files\n" 01182 "for source (using wildcards or file1+file2+file3 format).\n"); 01183 MSG_Add("SHELL_CMD_CALL_HELP","Starts a batch file from within another batch file.\n"); 01184 MSG_Add("SHELL_CMD_CALL_HELP_LONG","CALL [drive:][path]filename [batch-parameters]\n\n" 01185 "batch-parameters Specifies any command-line information required by\n" 01186 " the batch program.\n"); 01187 MSG_Add("SHELL_CMD_SUBST_HELP","Assigns an internal directory to a drive.\n"); 01188 MSG_Add("SHELL_CMD_SUBST_HELP_LONG","SUBST [drive1: [drive2:]path]\nSUBST drive1: /D\n\n" 01189 " drive1:\tSpecifies a drive to which you want to assign a path.\n" 01190 " [drive2:]path\tSpecifies a mounted local drive and path you want to assign to.\n" 01191 " /D\t\tDeletes a mounted or substituted drive.\n\n" 01192 "Type SUBST with no parameters to display a list of mounted local drives.\n"); 01193 MSG_Add("SHELL_CMD_LOADHIGH_HELP","Loads a program into upper memory (requires XMS and UMB memory).\n"); 01194 MSG_Add("SHELL_CMD_LOADHIGH_HELP_LONG","LH\t\t[drive1:][path]filename [parameters]\nLOADHIGH\t[drive1:][path]filename [parameters]\n"); 01195 MSG_Add("SHELL_CMD_LS_HELP", "Lists directory contents.\n"); 01196 MSG_Add("SHELL_CMD_LS_HELP_LONG", "LS [drive:][path][filename] [/A] [/L] [/P] [/Z]\n\n" 01197 " /A\tLists hidden and system files also.\n" 01198 " /L\tLists names one per line.\n" 01199 " /P\tPauses after each screenful of information.\n" 01200 " /Z\tDisplays short names even if LFN support is available.\n"); 01201 MSG_Add("SHELL_CMD_CHOICE_HELP","Waits for a keypress and sets ERRORLEVEL.\n"); 01202 MSG_Add("SHELL_CMD_CHOICE_HELP_LONG","CHOICE [/C:choices] [/N] [/S] text\n" 01203 " /C[:]choices - Specifies allowable keys. Default is: yn.\n" 01204 " /N - Do not display the choices at end of prompt.\n" 01205 " /S - Enables case-sensitive choices to be selected.\n" 01206 " text - The text to display as a prompt.\n"); 01207 MSG_Add("SHELL_CMD_ATTRIB_HELP","Displays or changes file attributes.\n"); 01208 MSG_Add("SHELL_CMD_ATTRIB_HELP_LONG","ATTRIB [+R | -R] [+A | -A] [+S | -S] [+H | -H] [drive:][path][filename] [/S]\n\n" 01209 " + Sets an attribute.\n" 01210 " - Clears an attribute.\n" 01211 " R Read-only file attribute.\n" 01212 " A Archive file attribute.\n" 01213 " S System file attribute.\n" 01214 " H Hidden file attribute.\n" 01215 " [drive:][path][filename] Specifies file(s) for ATTRIB to process.\n" 01216 " /S Processes files in all directories in the specified path.\n"); 01217 MSG_Add("SHELL_CMD_PATH_HELP","Displays or sets a search path for executable files.\n"); 01218 MSG_Add("SHELL_CMD_PATH_HELP_LONG","PATH [[drive:]path[;...][;%PATH%]\n" 01219 "PATH ;\n\n" 01220 "Type PATH ; to clear all search path settings.\n" 01221 "Type PATH without parameters to display the current path.\n"); 01222 MSG_Add("SHELL_CMD_VERIFY_HELP","Controls whether to verify files are written correctly to a disk.\n"); 01223 MSG_Add("SHELL_CMD_VERIFY_HELP_LONG","VERIFY [ON | OFF]\n\nType VERIFY without a parameter to display the current VERIFY setting.\n"); 01224 MSG_Add("SHELL_CMD_VER_HELP","Displays or sets DOSBox-X's reported DOS version.\n"); 01225 MSG_Add("SHELL_CMD_VER_HELP_LONG","VER [/R]\n" 01226 "VER SET [major.minor] or VER SET [major minor]\n\n" 01227 " [major.minor] or [major minor] Set the reported DOS version.\n\n" 01228 " Example: \"VER SET 6.0\" or \"VER SET 7.1\" for DOS 6.0 or 7.1 respectively.\n" 01229 " The command \"VER SET 7 1\" however sets the reported DOS version as 7.01.\n\n" 01230 "Type VER without parameters to display DOSBox-X and the reported DOS version.\n"); 01231 MSG_Add("SHELL_CMD_VER_VER","DOSBox-X version %s (%s). Reported DOS version %d.%02d.\n"); 01232 MSG_Add("SHELL_CMD_ADDKEY_HELP","Generates artificial keypresses.\n"); 01233 MSG_Add("SHELL_CMD_ADDKEY_HELP_LONG","ADDKEY [key]\n"); 01234 MSG_Add("SHELL_CMD_VOL_HELP","Displays the disk volume label and serial number, if they exist.\n"); 01235 MSG_Add("SHELL_CMD_VOL_HELP_LONG","VOL [drive]\n"); 01236 MSG_Add("SHELL_CMD_PROMPT_HELP","Changes the command prompt.\n"); 01237 MSG_Add("SHELL_CMD_PROMPT_HELP_LONG","PROMPT [text]\n" 01238 " text Specifies a new command prompt.\n\n" 01239 "Prompt can be made up of normal characters and the following special codes:\n" 01240 " $A & (Ampersand)\n" 01241 " $B | (pipe)\n" 01242 " $C ( (Left parenthesis)\n" 01243 " $D Current date\n" 01244 " $E Escape code (ASCII code 27)\n" 01245 " $F ) (Right parenthesis)\n" 01246 " $G > (greater-than sign)\n" 01247 " $H Backspace (erases previous character)\n" 01248 " $L < (less-than sign)\n" 01249 " $N Current drive\n" 01250 " $P Current drive and path\n" 01251 " $Q = (equal sign)\n" 01252 " $S (space)\n" 01253 " $T Current time\n" 01254 " $V DOS version number\n" 01255 " $_ Carriage return and linefeed\n" 01256 " $$ $ (dollar sign)\n"); 01257 MSG_Add("SHELL_CMD_ALIAS_HELP", "Defines or displays aliases.\n"); 01258 MSG_Add("SHELL_CMD_ALIAS_HELP_LONG", "ALIAS [name[=value] ... ]\n\nType ALIAS without parameters to display the list of aliases in the form:\n`ALIAS NAME = VALUE'\n"); 01259 MSG_Add("SHELL_CMD_COUNTRY_HELP", "Displays or changes the current country.\n"); 01260 MSG_Add("SHELL_CMD_COUNTRY_HELP_LONG", "COUNTRY [nnn] \n\n nnn\tSpecifies a country code.\n"); 01261 MSG_Add("SHELL_CMD_CTTY_HELP","Changes the terminal device used to control the system.\n"); 01262 MSG_Add("SHELL_CMD_CTTY_HELP_LONG","CTTY device\n device\tThe terminal device to use, such as CON.\n"); 01263 MSG_Add("SHELL_CMD_MORE_HELP","Displays output one screen at a time.\n"); 01264 MSG_Add("SHELL_CMD_MORE_HELP_LONG","MORE [drive:][path][filename]\nMORE < [drive:][path]filename\ncommand-name | MORE [drive:][path][filename]\n"); 01265 MSG_Add("SHELL_CMD_TRUENAME_HELP","Finds the fully-expanded name for a file.\n"); 01266 MSG_Add("SHELL_CMD_TRUENAME_HELP_LONG","TRUENAME file\n"); 01267 MSG_Add("SHELL_CMD_DXCAPTURE_HELP","Runs program with video or audio capture.\n"); 01268 MSG_Add("SHELL_CMD_DXCAPTURE_HELP_LONG","DX-CAPTURE [/V|/-V] [/A|/-A] [/M|/-M] [command] [options]\n\nIt will start video or audio capture, run program, and then automatically stop capture when the program exits.\n"); 01269 #if C_DEBUG 01270 MSG_Add("SHELL_CMD_DEBUGBOX_HELP","Runs program and breaks into debugger at entry point.\n"); 01271 MSG_Add("SHELL_CMD_DEBUGBOX_HELP_LONG","DEBUGBOX [command] [options]\n"); 01272 MSG_Add("SHELL_CMD_INT2FDBG_HELP","Hooks INT 2Fh for debugging purposes.\n"); 01273 MSG_Add("SHELL_CMD_INT2FDBG_HELP_LONG","INT2FDBG [option]\n /I Installs hook\n\nIt will hook INT 2Fh at the top of the call chain for debugging information.\n"); 01274 #endif 01275 MSG_Add("SHELL_CMD_COMMAND_HELP","Starts the DOSBox-X command shell.\n\nThe following options are accepted:\n\n /C\tExecutes the specified command and returns.\n /K\tExecutes the specified command and continues running.\n /INIT\tInitializes the command shell.\n"); 01276 01277 /* Regular startup */ 01278 call_shellstop=CALLBACK_Allocate(); 01279 /* Setup the startup CS:IP to kill the last running machine when exitted */ 01280 RealPt newcsip=CALLBACK_RealPointer(call_shellstop); 01281 SegSet16(cs,RealSeg(newcsip)); 01282 reg_ip=RealOff(newcsip); 01283 01284 CALLBACK_Setup(call_shellstop,shellstop_handler,CB_IRET,"shell stop"); 01285 PROGRAMS_MakeFile("COMMAND.COM",SHELL_ProgramStart); 01286 01287 /* NTS: Some DOS programs behave badly if run from a command interpreter 01288 * who's PSP segment is too low in memory and does not appear in 01289 * the MCB chain (SimCity 2000). So allocate shell memory normally 01290 * as any DOS application would do. 01291 * 01292 * That includes allocating COMMAND.COM stack NORMALLY (not up in 01293 * the UMB as DOSBox SVN would do) */ 01294 01295 /* Now call up the shell for the first time */ 01296 Bit16u psp_seg;//=DOS_FIRST_SHELL; 01297 Bit16u env_seg;//=DOS_FIRST_SHELL+19; //DOS_GetMemory(1+(4096/16))+1; 01298 Bit16u stack_seg;//=DOS_GetMemory(2048/16,"COMMAND.COM stack"); 01299 Bit16u tmp,total_sz; 01300 01301 // decide shell env size 01302 if (dosbox_shell_env_size == 0) 01303 dosbox_shell_env_size = (0x158u - (0x118u + 19u)) << 4u; /* equivalent to mainline DOSBox */ 01304 else 01305 dosbox_shell_env_size = (dosbox_shell_env_size+15u)&(~15u); /* round up to paragraph */ 01306 01307 LOG_MSG("COMMAND.COM env size: %u bytes",dosbox_shell_env_size); 01308 01309 // According to some sources, 0x0008 is a special PSP segment value used by DOS before the first 01310 // program is used. We need the current PSP segment to be nonzero so that DOS_AllocateMemory() 01311 // can properly allocate memory. 01312 dos.psp(8); 01313 01314 // COMMAND.COM environment block 01315 tmp = dosbox_shell_env_size>>4; 01316 if (!DOS_AllocateMemory(&env_seg,&tmp)) E_Exit("COMMAND.COM failed to allocate environment block segment"); 01317 LOG_MSG("COMMAND.COM environment block: 0x%04x sz=0x%04x",env_seg,tmp); 01318 01319 // COMMAND.COM main binary (including PSP and stack) 01320 tmp = 0x1A + (2048/16); 01321 total_sz = tmp; 01322 if (!DOS_AllocateMemory(&psp_seg,&tmp)) E_Exit("COMMAND.COM failed to allocate main body + PSP segment"); 01323 LOG_MSG("COMMAND.COM main body (PSP): 0x%04x sz=0x%04x",psp_seg,tmp); 01324 01325 // now COMMAND.COM has a main body and PSP segment, reflect it 01326 dos.psp(psp_seg); 01327 shell_psp = psp_seg; 01328 01329 { 01330 DOS_MCB mcb((Bit16u)(env_seg-1)); 01331 mcb.SetPSPSeg(psp_seg); 01332 mcb.SetFileName("COMMAND"); 01333 } 01334 01335 { 01336 DOS_MCB mcb((Bit16u)(psp_seg-1)); 01337 mcb.SetPSPSeg(psp_seg); 01338 mcb.SetFileName("COMMAND"); 01339 } 01340 01341 // set the stack at 0x1A 01342 stack_seg = psp_seg + 0x1A; 01343 LOG_MSG("COMMAND.COM stack: 0x%04x",stack_seg); 01344 01345 // set the stack pointer 01346 SegSet16(ss,stack_seg); 01347 reg_sp=2046; 01348 01349 /* Set up int 24 and psp (Telarium games) */ 01350 real_writeb(psp_seg+16+1,0,0xea); /* far jmp */ 01351 real_writed(psp_seg+16+1,1,real_readd(0,0x24*4)); 01352 real_writed(0,0x24*4,((Bit32u)psp_seg<<16) | ((16+1)<<4)); 01353 01354 /* Set up int 23 to "int 20" in the psp. Fixes what.exe */ 01355 real_writed(0,0x23*4,((Bit32u)psp_seg<<16)); 01356 01357 /* Set up int 2e handler */ 01358 if (call_int2e == 0) 01359 call_int2e = CALLBACK_Allocate(); 01360 01361 // RealPt addr_int2e=RealMake(psp_seg+16+1,8); 01362 // NTS: It's apparently common practice to enumerate MCBs by reading the segment value of INT 2Eh and then 01363 // scanning forward from there. The assumption seems to be that COMMAND.COM writes INT 2Eh there using 01364 // it's PSP segment and an offset like that of a COM executable even though COMMAND.COM is often an EXE file. 01365 RealPt addr_int2e=RealMake(psp_seg,8+((16+1)*16)); 01366 01367 CALLBACK_Setup(call_int2e,&INT2E_Handler,CB_IRET_STI,Real2Phys(addr_int2e),"Shell Int 2e"); 01368 RealSetVec(0x2e,addr_int2e); 01369 01370 /* Setup environment */ 01371 PhysPt env_write=PhysMake(env_seg,0); 01372 MEM_BlockWrite(env_write,path_string,(Bitu)(strlen(path_string)+1)); 01373 env_write += (PhysPt)(strlen(path_string)+1); 01374 MEM_BlockWrite(env_write,comspec_string,(Bitu)(strlen(comspec_string)+1)); 01375 env_write += (PhysPt)(strlen(comspec_string)+1); 01376 MEM_BlockWrite(env_write,prompt_string,(Bitu)(strlen(prompt_string)+1)); 01377 env_write +=(PhysPt)(strlen(prompt_string)+1); 01378 mem_writeb(env_write++,0); 01379 mem_writew(env_write,1); 01380 env_write+=2; 01381 MEM_BlockWrite(env_write,full_name,(Bitu)(strlen(full_name)+1)); 01382 01383 // extern bool Mouse_Vertical; 01384 extern bool Mouse_Drv; 01385 Mouse_Drv = true; 01386 01387 VFILE_RegisterBuiltinFileBlob(bfb_DEBUG_EXE); 01388 VFILE_RegisterBuiltinFileBlob(bfb_MOVE_EXE); 01389 VFILE_RegisterBuiltinFileBlob(bfb_FIND_EXE); 01390 VFILE_RegisterBuiltinFileBlob(bfb_LASTDRIV_COM); 01391 VFILE_RegisterBuiltinFileBlob(bfb_FCBS_COM); 01392 VFILE_RegisterBuiltinFileBlob(bfb_XCOPY_EXE); 01393 VFILE_RegisterBuiltinFileBlob(bfb_APPEND_EXE); 01394 VFILE_RegisterBuiltinFileBlob(bfb_DEVICE_COM); 01395 VFILE_RegisterBuiltinFileBlob(bfb_BUFFERS_COM); 01396 01397 /* These are IBM PC/XT/AT ONLY. They will not work in PC-98 mode. */ 01398 if (!IS_PC98_ARCH) { 01399 VFILE_RegisterBuiltinFileBlob(bfb_HEXMEM16_EXE); 01400 VFILE_RegisterBuiltinFileBlob(bfb_HEXMEM32_EXE); 01401 VFILE_RegisterBuiltinFileBlob(bfb_DOSIDLE_EXE); 01402 VFILE_RegisterBuiltinFileBlob(bfb_CWSDPMI_EXE); 01403 VFILE_RegisterBuiltinFileBlob(bfb_DOS32A_EXE); 01404 VFILE_RegisterBuiltinFileBlob(bfb_DOS4GW_EXE); 01405 VFILE_RegisterBuiltinFileBlob(bfb_EDIT_COM); 01406 VFILE_RegisterBuiltinFileBlob(bfb_TREE_EXE); 01407 01408 if (IS_VGA_ARCH) 01409 VFILE_RegisterBuiltinFileBlob(bfb_25_COM); 01410 else if (IS_EGA_ARCH) 01411 VFILE_RegisterBuiltinFileBlob(bfb_25_COM_ega); 01412 else 01413 VFILE_RegisterBuiltinFileBlob(bfb_25_COM_other); 01414 } 01415 01416 /* don't register 28.com unless EGA/VGA */ 01417 if (IS_VGA_ARCH) 01418 VFILE_RegisterBuiltinFileBlob(bfb_28_COM); 01419 else if (IS_EGA_ARCH) 01420 VFILE_RegisterBuiltinFileBlob(bfb_28_COM_ega); 01421 01422 /* don't register 50 unless VGA */ 01423 if (IS_VGA_ARCH) VFILE_RegisterBuiltinFileBlob(bfb_50_COM); 01424 01425 /* MEM.COM is not compatible with PC-98 and/or 8086 emulation */ 01426 if (!IS_PC98_ARCH && CPU_ArchitectureType >= CPU_ARCHTYPE_80186) 01427 VFILE_RegisterBuiltinFileBlob(bfb_MEM_COM); 01428 01429 /* DSXMENU.EXE */ 01430 if (IS_PC98_ARCH) 01431 VFILE_RegisterBuiltinFileBlob(bfb_DSXMENU_EXE_PC98); 01432 else 01433 VFILE_RegisterBuiltinFileBlob(bfb_DSXMENU_EXE_PC); 01434 01435 DOS_PSP psp(psp_seg); 01436 psp.MakeNew(0); 01437 dos.psp(psp_seg); 01438 01439 /* The start of the filetable in the psp must look like this: 01440 * 01 01 01 00 02 01441 * In order to achieve this: First open 2 files. Close the first and 01442 * duplicate the second (so the entries get 01) */ 01443 Bit16u dummy=0; 01444 DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDIN */ 01445 DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDOUT */ 01446 DOS_CloseFile(0); /* Close STDIN */ 01447 DOS_ForceDuplicateEntry(1,0); /* "new" STDIN */ 01448 DOS_ForceDuplicateEntry(1,2); /* STDERR */ 01449 DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDAUX */ 01450 if (!DOS_OpenFile("PRN",OPEN_READWRITE,&dummy)) DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDPRN */ 01451 01452 psp.SetSize(psp_seg + total_sz); 01453 psp.SetStack(((unsigned int)stack_seg << 16u) + (unsigned int)reg_sp); 01454 psp.SetParent(psp_seg); 01455 /* Set the environment */ 01456 psp.SetEnvironment(env_seg); 01457 /* Set the command line for the shell start up */ 01458 CommandTail tail; 01459 tail.count=(Bit8u)strlen(init_line); 01460 memset(&tail.buffer, 0, CTBUF); 01461 strncpy(tail.buffer,init_line,CTBUF); 01462 MEM_BlockWrite(PhysMake(psp_seg,CTBUF+1),&tail,CTBUF+1); 01463 01464 /* Setup internal DOS Variables */ 01465 dos.dta(RealMake(psp_seg,CTBUF+1)); 01466 dos.psp(psp_seg); 01467 01468 /* settings */ 01469 { 01470 const Section_prop * section=static_cast<Section_prop *>(control->GetSection("dos")); 01471 enable_config_as_shell_commands = section->Get_bool("shell configuration as commands"); 01472 } 01473 } 01474 01475 /* Pfff... starting and running the shell from a configuration section INIT 01476 * What the hell were you guys thinking? --J.C. */ 01477 void SHELL_Run() { 01478 dos_shell_running_program = false; 01479 #if defined(WIN32) && !defined(C_SDL2) 01480 int Reflect_Menu(void); 01481 Reflect_Menu(); 01482 #endif 01483 01484 LOG(LOG_MISC,LOG_DEBUG)("Running DOS shell now"); 01485 01486 if (first_shell != NULL) E_Exit("Attempt to start shell when shell already running"); 01487 SHELL_ProgramStart_First_shell(&first_shell); 01488 01489 try { 01490 first_shell->Run(); 01491 delete first_shell; 01492 first_shell = 0;//Make clear that it shouldn't be used anymore 01493 dos_shell_running_program = false; 01494 #if defined(WIN32) && !defined(C_SDL2) 01495 int Reflect_Menu(void); 01496 Reflect_Menu(); 01497 #endif 01498 } 01499 catch (...) { 01500 delete first_shell; 01501 first_shell = 0;//Make clear that it shouldn't be used anymore 01502 dos_shell_running_program = false; 01503 #if defined(WIN32) && !defined(C_SDL2) 01504 int Reflect_Menu(void); 01505 Reflect_Menu(); 01506 #endif 01507 throw; 01508 } 01509 }