DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/shell/shell_cmds.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 
00020 #include "dosbox.h"
00021 #include "shell.h"
00022 #include "callback.h"
00023 #include "regs.h"
00024 #include "pic.h"
00025 #include "keyboard.h"
00026 #include "timer.h"
00027 #include "../src/ints/int10.h"
00028 #include <time.h>
00029 #include <assert.h>
00030 #include "bios.h"
00031 #include "../dos/drives.h"
00032 #include "support.h"
00033 #include "control.h"
00034 #include <algorithm>
00035 #include <cstring>
00036 #include <cctype>
00037 #include <cstdlib>
00038 #include <vector>
00039 #include <string>
00040 #include <time.h>
00041 
00042 #if defined(_MSC_VER)
00043 # pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */
00044 #endif
00045 
00046 static SHELL_Cmd cmd_list[]={
00047 {       "DIR",          0,                      &DOS_Shell::CMD_DIR,            "SHELL_CMD_DIR_HELP"},
00048 {       "CHDIR",        1,                      &DOS_Shell::CMD_CHDIR,          "SHELL_CMD_CHDIR_HELP"},
00049 {       "ATTRIB",       1,                      &DOS_Shell::CMD_ATTRIB,         "SHELL_CMD_ATTRIB_HELP"},
00050 {       "CALL",         1,                      &DOS_Shell::CMD_CALL,           "SHELL_CMD_CALL_HELP"},
00051 {       "CD",           0,                      &DOS_Shell::CMD_CHDIR,          "SHELL_CMD_CHDIR_HELP"},
00052 {       "CHOICE",       1,                      &DOS_Shell::CMD_CHOICE,         "SHELL_CMD_CHOICE_HELP"},
00053 {       "CLS",          0,                      &DOS_Shell::CMD_CLS,            "SHELL_CMD_CLS_HELP"},
00054 {       "COPY",         0,                      &DOS_Shell::CMD_COPY,           "SHELL_CMD_COPY_HELP"},
00055 {       "DATE",         0,                      &DOS_Shell::CMD_DATE,           "SHELL_CMD_DATE_HELP"},
00056 {       "DEL",          0,                      &DOS_Shell::CMD_DELETE,         "SHELL_CMD_DELETE_HELP"},
00057 {       "DELETE",       1,                      &DOS_Shell::CMD_DELETE,         "SHELL_CMD_DELETE_HELP"},
00058 {       "ERASE",        1,                      &DOS_Shell::CMD_DELETE,         "SHELL_CMD_DELETE_HELP"},
00059 {       "ECHO",         1,                      &DOS_Shell::CMD_ECHO,           "SHELL_CMD_ECHO_HELP"},
00060 {       "EXIT",         0,                      &DOS_Shell::CMD_EXIT,           "SHELL_CMD_EXIT_HELP"}, 
00061 {       "GOTO",         1,                      &DOS_Shell::CMD_GOTO,           "SHELL_CMD_GOTO_HELP"},
00062 {       "HELP",         1,                      &DOS_Shell::CMD_HELP,           "SHELL_CMD_HELP_HELP"},
00063 {       "IF",           1,                      &DOS_Shell::CMD_IF,                     "SHELL_CMD_IF_HELP"},
00064 {       "LOADHIGH",     1,                      &DOS_Shell::CMD_LOADHIGH,       "SHELL_CMD_LOADHIGH_HELP"},
00065 {       "LH",           1,                      &DOS_Shell::CMD_LOADHIGH,       "SHELL_CMD_LOADHIGH_HELP"},
00066 {       "MKDIR",        1,                      &DOS_Shell::CMD_MKDIR,          "SHELL_CMD_MKDIR_HELP"},
00067 {       "MD",           0,                      &DOS_Shell::CMD_MKDIR,          "SHELL_CMD_MKDIR_HELP"},
00068 {       "PATH",         1,                      &DOS_Shell::CMD_PATH,           "SHELL_CMD_PATH_HELP"},
00069 {       "PAUSE",        1,                      &DOS_Shell::CMD_PAUSE,          "SHELL_CMD_PAUSE_HELP"},
00070 {       "RMDIR",        1,                      &DOS_Shell::CMD_RMDIR,          "SHELL_CMD_RMDIR_HELP"},
00071 {       "RD",           0,                      &DOS_Shell::CMD_RMDIR,          "SHELL_CMD_RMDIR_HELP"},
00072 {       "REM",          1,                      &DOS_Shell::CMD_REM,            "SHELL_CMD_REM_HELP"},
00073 {       "RENAME",       1,                      &DOS_Shell::CMD_RENAME,         "SHELL_CMD_RENAME_HELP"},
00074 {       "REN",          0,                      &DOS_Shell::CMD_RENAME,         "SHELL_CMD_RENAME_HELP"},
00075 {       "SET",          1,                      &DOS_Shell::CMD_SET,            "SHELL_CMD_SET_HELP"},
00076 {       "SHIFT",        1,                      &DOS_Shell::CMD_SHIFT,          "SHELL_CMD_SHIFT_HELP"},
00077 {       "SUBST",        1,                      &DOS_Shell::CMD_SUBST,          "SHELL_CMD_SUBST_HELP"},
00078 {       "TIME",         0,                      &DOS_Shell::CMD_TIME,           "SHELL_CMD_TIME_HELP"},
00079 {       "TYPE",         0,                      &DOS_Shell::CMD_TYPE,           "SHELL_CMD_TYPE_HELP"},
00080 {       "VER",          0,                      &DOS_Shell::CMD_VER,            "SHELL_CMD_VER_HELP"},
00081 {       "ADDKEY",       1,                      &DOS_Shell::CMD_ADDKEY,         "SHELL_CMD_ADDKEY_HELP"},
00082 {       "VOL",  0,                      &DOS_Shell::CMD_VOL,            "SHELL_CMD_VOL_HELP"},
00083 {       "PROMPT",       0,                      &DOS_Shell::CMD_PROMPT,         "SHELL_CMD_PROMPT_HELP"},
00084 {       "LABEL",        0,                      &DOS_Shell::CMD_LABEL,          "SHELL_CMD_LABEL_HELP"},
00085 {       "MORE", 1,                      &DOS_Shell::CMD_MORE,           "SHELL_CMD_MORE_HELP"},
00086 {       "FOR",  1,                      &DOS_Shell::CMD_FOR,            "SHELL_CMD_FOR_HELP"},
00087 {       "INT2FDBG",     1,                      &DOS_Shell::CMD_INT2FDBG,       "Hook INT 2Fh for debugging purposes"},
00088 {       "CTTY",         1,                      &DOS_Shell::CMD_CTTY,           "Change TTY device"},
00089 {   "DX-CAPTURE",0,         &DOS_Shell::CMD_DXCAPTURE,  "Run program with video/audio capture"},
00090 #if C_DEBUG
00091 {       "DEBUGBOX",     0,                      &DOS_Shell::CMD_DEBUGBOX,       "Run program, break into debugger at entry point"},
00092 #endif
00093 {0,0,0,0}
00094 }; 
00095 
00096 /* support functions */
00097 static char empty_char = 0;
00098 static char* empty_string = &empty_char;
00099 static void StripSpaces(char*&args) {
00100         while(args && *args && isspace(*reinterpret_cast<unsigned char*>(args)))
00101                 args++;
00102 }
00103 
00104 static void StripSpaces(char*&args,char also) {
00105         while(args && *args && (isspace(*reinterpret_cast<unsigned char*>(args)) || (*args == also)))
00106                 args++;
00107 }
00108 
00109 static char* ExpandDot(char*args, char* buffer) {
00110         if(*args == '.') {
00111                 if(*(args+1) == 0){
00112                         strcpy(buffer,"*.*");
00113                         return buffer;
00114                 }
00115                 if( (*(args+1) != '.') && (*(args+1) != '\\') ) {
00116                         buffer[0] = '*';
00117                         buffer[1] = 0;
00118                         strcat(buffer,args);
00119                         return buffer;
00120                 } else
00121                         strcpy (buffer, args);
00122         }
00123         else strcpy(buffer,args);
00124         return buffer;
00125 }
00126 
00127 
00128 
00129 bool DOS_Shell::CheckConfig(char* cmd_in,char*line) {
00130         Section* test = control->GetSectionFromProperty(cmd_in);
00131         if(!test) return false;
00132         if(line && !line[0]) {
00133                 std::string val = test->GetPropValue(cmd_in);
00134                 if(val != NO_SUCH_PROPERTY) WriteOut("%s\n",val.c_str());
00135                 return true;
00136         }
00137         char newcom[1024]; newcom[0] = 0; strcpy(newcom,"z:\\config -set ");
00138         strcat(newcom,test->GetName()); strcat(newcom," ");
00139         strcat(newcom,cmd_in);strcat(newcom,line);
00140         DoCommand(newcom);
00141         return true;
00142 }
00143 
00144 bool enable_config_as_shell_commands = false;
00145 
00146 void DOS_Shell::DoCommand(char * line) {
00147 /* First split the line into command and arguments */
00148         line=trim(line);
00149         char cmd_buffer[CMD_MAXLINE];
00150         char * cmd_write=cmd_buffer;
00151         while (*line) {
00152                 if (*line == 32) break;
00153                 if (*line == '/') break;
00154                 if (*line == '\t') break;
00155                 if (*line == '=') break;
00156 //              if (*line == ':') break; //This breaks drive switching as that is handled at a later stage. 
00157                 if ((*line == '.') ||(*line == '\\')) {  //allow stuff like cd.. and dir.exe cd\kees
00158                         *cmd_write=0;
00159                         Bit32u cmd_index=0;
00160                         while (cmd_list[cmd_index].name) {
00161                                 if (strcasecmp(cmd_list[cmd_index].name,cmd_buffer)==0) {
00162                                         (this->*(cmd_list[cmd_index].handler))(line);
00163                                         return;
00164                                 }
00165                                 cmd_index++;
00166                         }
00167                 }
00168                 *cmd_write++=*line++;
00169         }
00170         *cmd_write=0;
00171         if (strlen(cmd_buffer)==0) return;
00172 /* Check the internal list */
00173         Bit32u cmd_index=0;
00174         while (cmd_list[cmd_index].name) {
00175                 if (strcasecmp(cmd_list[cmd_index].name,cmd_buffer)==0) {
00176                         (this->*(cmd_list[cmd_index].handler))(line);
00177                         return;
00178                 }
00179                 cmd_index++;
00180         }
00181 /* This isn't an internal command execute it */
00182         if(Execute(cmd_buffer,line)) return;
00183         if(enable_config_as_shell_commands && CheckConfig(cmd_buffer,line)) return;
00184         WriteOut(MSG_Get("SHELL_EXECUTE_ILLEGAL_COMMAND"),cmd_buffer);
00185 }
00186 
00187 #define HELP(command) \
00188         if (ScanCMDBool(args,"?")) { \
00189                 WriteOut(MSG_Get("SHELL_CMD_" command "_HELP")); \
00190                 const char* long_m = MSG_Get("SHELL_CMD_" command "_HELP_LONG"); \
00191                 WriteOut("\n"); \
00192                 if(strcmp("Message not Found!\n",long_m)) WriteOut(long_m); \
00193                 else WriteOut(command "\n"); \
00194                 return; \
00195         }
00196 
00197 Bitu int2fdbg_hook_callback = 0;
00198 
00199 static Bitu INT2FDBG_Handler(void) {
00200         if (reg_ax == 0x1605) { /* Windows init broadcast */
00201                 int patience = 500;
00202                 Bitu st_seg,st_ofs;
00203 
00204                 LOG_MSG("INT 2Fh debug hook: Caught Windows init broadcast results (ES:BX=%04x:%04x DS:SI=%04x:%04x CX=%04x DX=%04x DI=%04x)\n",
00205                         SegValue(es),reg_bx,
00206                         SegValue(ds),reg_si,
00207                         reg_cx,reg_dx,reg_di);
00208 
00209                 st_seg = SegValue(es);
00210                 st_ofs = reg_bx;
00211                 while (st_seg != 0 || st_ofs != 0) {
00212                         unsigned char v_major,v_minor;
00213                         Bitu st_seg_next,st_ofs_next;
00214                         Bitu idrc_seg,idrc_ofs;
00215                         Bitu vdev_seg,vdev_ofs;
00216                         Bitu name_seg,name_ofs;
00217                         char devname[64];
00218                         PhysPt st_o;
00219 
00220                         if (--patience <= 0) {
00221                                 LOG_MSG("**WARNING: Chain is too long. Something might have gotten corrupted\n");
00222                                 break;
00223                         }
00224 
00225                         st_o = PhysMake(st_seg,st_ofs);
00226                         /* +0x00: Major, minor version of info structure
00227                          * +0x02: pointer to next startup info structure or 0000:0000
00228                          * +0x06: pointer to ASCIIZ name of virtual device or 0000:0000
00229                          * +0x0A: virtual device ref data (pointer to?? or actual data??) or 0000:0000
00230                          * +0x0E: pointer to instance data records or 0000:0000
00231                          * Windows 95 or later (v4.0+):
00232                          * +0x12: pointer to optionally-instanced data records or 0000:0000 */
00233                         v_major = mem_readb(st_o+0x00);
00234                         v_minor = mem_readb(st_o+0x01);
00235                         st_seg_next = mem_readw(st_o+0x02+2);
00236                         st_ofs_next = mem_readw(st_o+0x02+0);
00237                         name_ofs = mem_readw(st_o+0x06+0);
00238                         name_seg = mem_readw(st_o+0x06+2);
00239                         vdev_ofs = mem_readw(st_o+0x0A+0);
00240                         vdev_seg = mem_readw(st_o+0x0A+2);
00241                         idrc_ofs = mem_readw(st_o+0x0A+4);      /* FIXME: 0x0E+0 and 0x0E+2 generates weird compiler error WTF?? */
00242                         idrc_seg = mem_readw(st_o+0x0A+6);
00243 
00244                         {
00245                                 devname[0] = 0;
00246                                 if (name_seg != 0 || name_ofs != 0) {
00247                                         unsigned char c;
00248                                         unsigned int i;
00249                                         PhysPt scan;
00250 
00251                                         scan = PhysMake(name_seg,name_ofs);
00252                                         for (i=0;i < 63 && (c=mem_readb(scan++)) != 0;) devname[i++] = (char)c;
00253                                         devname[i] = 0;
00254                                 }
00255                         }
00256 
00257                         LOG_MSG(" >> Version %u.%u\n",v_major,v_minor);
00258                         LOG_MSG("    Next entry at %04x:%04x\n",(int)st_seg_next,(int)st_ofs_next);
00259                         LOG_MSG("    Virtual device name: %04x:%04x '%s'\n",(int)name_seg,(int)name_ofs,devname);
00260                         LOG_MSG("    Virtual dev ref data: %04x:%04x\n",(int)vdev_seg,(int)vdev_ofs);
00261                         LOG_MSG("    Instance data records: %04x:%04x\n",(int)idrc_seg,(int)idrc_ofs);
00262 
00263                         st_seg = st_seg_next;
00264                         st_ofs = st_ofs_next;
00265                 }
00266 
00267                 LOG_MSG("----END CHAIN\n");
00268         }
00269 
00270         return CBRET_NONE;
00271 }
00272 
00273 /* NTS: I know I could just modify the DOS kernel's INT 2Fh code to receive the init call,
00274  *      the problem is that at that point, the registers do not yet contain anything interesting.
00275  *      all the interesting results of the call are added by TSRs on the way back UP the call
00276  *      chain. The purpose of this program therefore is to hook INT 2Fh on the other end
00277  *      of the call chain so that we can see the results just before returning INT 2Fh back
00278  *      to WIN.COM */
00279 void DOS_Shell::CMD_INT2FDBG(char * args) {
00280         /* TODO: Allow /U to remove INT 2Fh hook */
00281 
00282         if (ScanCMDBool(args,"I")) {
00283                 if (int2fdbg_hook_callback == 0) {
00284                         Bit32u old_int2Fh;
00285                         PhysPt w;
00286 
00287                         int2fdbg_hook_callback = CALLBACK_Allocate();
00288                         CALLBACK_Setup(int2fdbg_hook_callback,&INT2FDBG_Handler,CB_IRET,"INT 2Fh DBG callback");
00289 
00290                         /* record old vector, set our new vector */
00291                         old_int2Fh = RealGetVec(0x2f);
00292                         w = CALLBACK_PhysPointer(int2fdbg_hook_callback);
00293                         RealSetVec(0x2f,CALLBACK_RealPointer(int2fdbg_hook_callback));
00294 
00295                         /* overwrite the callback with code to chain the call down, then invoke our callback on the way back up: */
00296 
00297                         /* first, chain to the previous INT 15h handler */
00298                         phys_writeb(w++,(Bit8u)0x9C);                                   //PUSHF
00299                         phys_writeb(w++,(Bit8u)0x9A);                                   //CALL FAR <address>
00300                         phys_writew(w,(Bit16u)(old_int2Fh&0xFFFF)); w += 2;             //offset
00301                         phys_writew(w,(Bit16u)((old_int2Fh>>16)&0xFFFF)); w += 2;       //seg
00302 
00303                         /* then, having returned from it, invoke our callback */
00304                         phys_writeb(w++,(Bit8u)0xFE);                                   //GRP 4
00305                         phys_writeb(w++,(Bit8u)0x38);                                   //Extra Callback instruction
00306                         phys_writew(w,(Bit16u)int2fdbg_hook_callback); w += 2;          //The immediate word
00307 
00308                         /* return */
00309                         phys_writeb(w++,(Bit8u)0xCF);                                   //IRET
00310 
00311                         LOG_MSG("INT 2Fh debugging hook set\n");
00312                         WriteOut("INT 2Fh hook set\n");
00313                 }
00314                 else {
00315                         WriteOut("INT 2Fh hook already setup\n");
00316                 }
00317         }
00318         else {
00319                 WriteOut("INT2FDBG [switches]\n");
00320                 WriteOut("INT2FDBG.COM Int 2Fh debugging hook.\n");
00321                 WriteOut("  /I      Install hook\n");
00322                 WriteOut("\n");
00323                 WriteOut("Hooks INT 2Fh at the top of the call chain for debugging information.\n");
00324         }
00325 }
00326 
00327 void DOS_Shell::CMD_CLS(char * args) {
00328         HELP("CLS");
00329    if (CurMode->type==M_TEXT || IS_PC98_ARCH)
00330        WriteOut("");
00331    else { 
00332       reg_ax=(Bit16u)CurMode->mode; 
00333       CALLBACK_RunRealInt(0x10); 
00334    } 
00335 }
00336 
00337 void DOS_Shell::CMD_DELETE(char * args) {
00338         HELP("DELETE");
00339         bool optQ1=ScanCMDBool(args,"Q");
00340 
00341         // ignore /p /f, /s, /ar, /as, /ah and /aa switches for compatibility
00342         ScanCMDBool(args,"P");
00343         ScanCMDBool(args,"F");
00344         ScanCMDBool(args,"S");
00345         ScanCMDBool(args,"AR");
00346         ScanCMDBool(args,"AS");
00347         ScanCMDBool(args,"AH");
00348         ScanCMDBool(args,"AA");
00349 
00350         StripSpaces(args);
00351         if (!strcasecmp(args,"*")) args=(char*)("*.*"); // 'del *' should be 'del *.*'?
00352         if (!strcasecmp(args,"*.*")) {
00353                 if (!optQ1) {
00354 first_1:
00355                         WriteOut(MSG_Get("SHELL_CMD_DEL_SURE"));
00356 first_2:
00357                         Bit8u c;Bit16u n=1;
00358                         DOS_ReadFile (STDIN,&c,&n);
00359                         do switch (c) {
00360                         case 'n':                       case 'N':
00361                         {
00362                                 DOS_WriteFile (STDOUT,&c, &n);
00363                                 DOS_ReadFile (STDIN,&c,&n);
00364                                 do switch (c) {
00365                                         case 0xD: WriteOut("\n"); return;
00366                                         case 0x08: WriteOut("\b \b"); goto first_2;
00367                                 } while (DOS_ReadFile (STDIN,&c,&n));
00368                         }
00369                         case 'y':                       case 'Y':
00370                         {
00371                                 DOS_WriteFile (STDOUT,&c, &n);
00372                                 DOS_ReadFile (STDIN,&c,&n);
00373                                 do switch (c) {
00374                                         case 0xD: WriteOut("\n"); goto continue_1;
00375                                         case 0x08: WriteOut("\b \b"); goto first_2;
00376                                 } while (DOS_ReadFile (STDIN,&c,&n));
00377                         }
00378                         case 0xD: WriteOut("\n"); goto first_1;
00379                         case '\t':
00380                         case 0x08:
00381                                 goto first_2;
00382                         default:
00383                         {
00384                                 DOS_WriteFile (STDOUT,&c, &n);
00385                                 DOS_ReadFile (STDIN,&c,&n);
00386                                 do switch (c) {
00387                                         case 0xD: WriteOut("\n"); goto first_1;
00388                                         case 0x08: WriteOut("\b \b"); goto first_2;
00389                                 } while (DOS_ReadFile (STDIN,&c,&n));
00390                                 goto first_2;
00391                         }
00392                 } while (DOS_ReadFile (STDIN,&c,&n));
00393         }
00394 }
00395 
00396 continue_1:
00397         /* Command uses dta so set it to our internal dta */
00398         RealPt save_dta=dos.dta();
00399         dos.dta(dos.tables.tempdta);
00400 
00401         char * rem=ScanCMDRemain(args);
00402         if (rem) {
00403                 WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
00404                 return;
00405         }
00406         /* If delete accept switches mind the space infront of them. See the dir /p code */ 
00407 
00408         char full[DOS_PATHLENGTH];
00409         char buffer[CROSS_LEN];
00410         args = ExpandDot(args,buffer);
00411         StripSpaces(args);
00412         if (!DOS_Canonicalize(args,full)) { WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));return; }
00413         bool res=DOS_FindFirst(args,0xffff & ~DOS_ATTR_VOLUME);
00414         if (!res) {
00415                 WriteOut(MSG_Get("SHELL_CMD_DEL_ERROR"),args);
00416                 dos.dta(save_dta);
00417                 return;
00418         }
00419         //end can't be 0, but if it is we'll get a nice crash, who cares :)
00420         char * end=strrchr(full,'\\')+1;*end=0;
00421         char name[DOS_NAMELENGTH_ASCII];Bit32u size;Bit16u time,date;Bit8u attr;
00422         DOS_DTA dta(dos.dta());
00423         while (res) {
00424                 dta.GetResult(name,size,date,time,attr);        
00425                 if (!(attr & (DOS_ATTR_DIRECTORY|DOS_ATTR_READ_ONLY))) {
00426                         strcpy(end,name);
00427                         if (!DOS_UnlinkFile(full)) WriteOut(MSG_Get("SHELL_CMD_DEL_ERROR"),full);
00428                 }
00429                 res=DOS_FindNext();
00430         }
00431         dos.dta(save_dta);
00432 }
00433 
00434 void DOS_Shell::CMD_HELP(char * args){
00435         HELP("HELP");
00436         bool optall=ScanCMDBool(args,"ALL");
00437         /* Print the help */
00438         if(!optall) WriteOut(MSG_Get("SHELL_CMD_HELP"));
00439         Bit32u cmd_index=0,write_count=0;
00440         while (cmd_list[cmd_index].name) {
00441                 if (optall || !cmd_list[cmd_index].flags) {
00442                         WriteOut("<\033[34;1m%-8s\033[0m> %s",cmd_list[cmd_index].name,MSG_Get(cmd_list[cmd_index].help));
00443                         if(!(++write_count%22)) CMD_PAUSE(empty_string);
00444                 }
00445                 cmd_index++;
00446         }
00447 }
00448 
00449 void DOS_Shell::CMD_RENAME(char * args){
00450         HELP("RENAME");
00451         StripSpaces(args);
00452         if(!*args) {SyntaxError();return;}
00453         if((strchr(args,'*')!=NULL) || (strchr(args,'?')!=NULL) ) { WriteOut(MSG_Get("SHELL_CMD_NO_WILD"));return;}
00454         char * arg1=StripWord(args);
00455         char* slash = strrchr(arg1,'\\');
00456         if(slash) { 
00457                 slash++;
00458                 /* If directory specified (crystal caves installer)
00459                  * rename from c:\X : rename c:\abc.exe abc.shr. 
00460                  * File must appear in C:\ */ 
00461                 
00462                 char dir_source[DOS_PATHLENGTH]={0};
00463                 //Copy first and then modify, makes GCC happy
00464                 strcpy(dir_source,arg1);
00465                 char* dummy = strrchr(dir_source,'\\');
00466                 *dummy=0;
00467 
00468                 if((strlen(dir_source) == 2) && (dir_source[1] == ':')) 
00469                         strcat(dir_source,"\\"); //X: add slash
00470 
00471                 char dir_current[DOS_PATHLENGTH + 1];
00472                 dir_current[0] = '\\'; //Absolute addressing so we can return properly
00473                 DOS_GetCurrentDir(0,dir_current + 1);
00474                 if(!DOS_ChangeDir(dir_source)) {
00475                         WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
00476                         return;
00477                 }
00478                 DOS_Rename(slash,args);
00479                 DOS_ChangeDir(dir_current);
00480         } else {
00481                 DOS_Rename(arg1,args);
00482         }
00483 }
00484 
00485 void DOS_Shell::CMD_ECHO(char * args){
00486         if (!*args) {
00487                 if (echo) { WriteOut(MSG_Get("SHELL_CMD_ECHO_ON"));}
00488                 else { WriteOut(MSG_Get("SHELL_CMD_ECHO_OFF"));}
00489                 return;
00490         }
00491         char buffer[512];
00492         char* pbuffer = buffer;
00493         safe_strncpy(buffer,args,512);
00494         StripSpaces(pbuffer);
00495         if (strcasecmp(pbuffer,"OFF")==0) {
00496                 echo=false;             
00497                 return;
00498         }
00499         if (strcasecmp(pbuffer,"ON")==0) {
00500                 echo=true;              
00501                 return;
00502         }
00503         if(strcasecmp(pbuffer,"/?")==0) { HELP("ECHO"); }
00504 
00505         args++;//skip first character. either a slash or dot or space
00506         size_t len = strlen(args); //TODO check input of else ook nodig is.
00507         if(len && args[len - 1] == '\r') {
00508                 LOG(LOG_MISC,LOG_WARN)("Hu ? carriage return already present. Is this possible?");
00509                 WriteOut("%s\n",args);
00510         } else WriteOut("%s\r\n",args);
00511 }
00512 
00513 
00514 void DOS_Shell::CMD_EXIT(char * args) {
00515         HELP("EXIT");
00516         exit = true;
00517 }
00518 
00519 void DOS_Shell::CMD_CHDIR(char * args) {
00520         HELP("CHDIR");
00521         StripSpaces(args);
00522         Bit8u drive = DOS_GetDefaultDrive()+'A';
00523         char dir[DOS_PATHLENGTH];
00524         if (!*args) {
00525                 DOS_GetCurrentDir(0,dir);
00526                 WriteOut("%c:\\%s\n",drive,dir);
00527         } else if(strlen(args) == 2 && args[1]==':') {
00528                 Bit8u targetdrive = (args[0] | 0x20)-'a' + 1;
00529                 unsigned char targetdisplay = *reinterpret_cast<unsigned char*>(&args[0]);
00530                 if(!DOS_GetCurrentDir(targetdrive,dir)) {
00531                         if(drive == 'Z') {
00532                                 WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_NOT_FOUND"),toupper(targetdisplay));
00533                         } else {
00534                                 WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
00535                         }
00536                         return;
00537                 }
00538                 WriteOut("%c:\\%s\n",toupper(targetdisplay),dir);
00539                 if(drive == 'Z')
00540                         WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT"),toupper(targetdisplay));
00541         } else  if (!DOS_ChangeDir(args)) {
00542                 /* Changedir failed. Check if the filename is longer then 8 and/or contains spaces */
00543            
00544                 std::string temps(args),slashpart;
00545                 std::string::size_type separator = temps.find_first_of("\\/");
00546                 if(!separator) {
00547                         slashpart = temps.substr(0,1);
00548                         temps.erase(0,1);
00549                 }
00550                 separator = temps.find_first_of("\\/");
00551                 if(separator != std::string::npos) temps.erase(separator);
00552                 separator = temps.rfind('.');
00553                 if(separator != std::string::npos) temps.erase(separator);
00554                 separator = temps.find(' ');
00555                 if(separator != std::string::npos) {/* Contains spaces */
00556                         temps.erase(separator);
00557                         if(temps.size() >6) temps.erase(6);
00558                         temps += "~1";
00559                         WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT_2"),temps.insert(0,slashpart).c_str());
00560                 } else if (temps.size()>8) {
00561                         temps.erase(6);
00562                         temps += "~1";
00563                         WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT_2"),temps.insert(0,slashpart).c_str());
00564                 } else {
00565                         if (drive == 'Z') {
00566                                 WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT_3"));
00567                         } else {
00568                                 WriteOut(MSG_Get("SHELL_CMD_CHDIR_ERROR"),args);
00569                         }
00570                 }
00571         }
00572 }
00573 
00574 void DOS_Shell::CMD_MKDIR(char * args) {
00575         HELP("MKDIR");
00576         StripSpaces(args);
00577         char * rem=ScanCMDRemain(args);
00578         if (rem) {
00579                 WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
00580                 return;
00581         }
00582         if (!DOS_MakeDir(args)) {
00583                 WriteOut(MSG_Get("SHELL_CMD_MKDIR_ERROR"),args);
00584         }
00585 }
00586 
00587 void DOS_Shell::CMD_RMDIR(char * args) {
00588         HELP("RMDIR");
00589         // ignore /s,and /q switches for compatibility
00590         ScanCMDBool(args,"S");
00591         ScanCMDBool(args,"Q");
00592         StripSpaces(args);
00593         char * rem=ScanCMDRemain(args);
00594         if (rem) {
00595                 WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
00596                 return;
00597         }
00598         if (!DOS_RemoveDir(args)) {
00599                 WriteOut(MSG_Get("SHELL_CMD_RMDIR_ERROR"),args);
00600         }
00601 }
00602 
00603 static void FormatNumber(Bit32u num,char * buf) {
00604         Bit32u numm,numk,numb,numg;
00605         numb=num % 1000;
00606         num/=1000;
00607         numk=num % 1000;
00608         num/=1000;
00609         numm=num % 1000;
00610         num/=1000;
00611         numg=num;
00612         if (numg) {
00613                 sprintf(buf,"%d,%03d,%03d,%03d",numg,numm,numk,numb);
00614                 return;
00615         };
00616         if (numm) {
00617                 sprintf(buf,"%d,%03d,%03d",numm,numk,numb);
00618                 return;
00619         };
00620         if (numk) {
00621                 sprintf(buf,"%d,%03d",numk,numb);
00622                 return;
00623         };
00624         sprintf(buf,"%d",numb);
00625 }
00626 
00627 struct DtaResult {
00628         char name[DOS_NAMELENGTH_ASCII];
00629         Bit32u size;
00630         Bit16u date;
00631         Bit16u time;
00632         Bit8u attr;
00633 
00634         static bool compareName(const DtaResult &lhs, const DtaResult &rhs) { return strcmp(lhs.name, rhs.name) < 0; }
00635         static bool compareExt(const DtaResult &lhs, const DtaResult &rhs) { return strcmp(lhs.getExtension(), rhs.getExtension()) < 0; }
00636         static bool compareSize(const DtaResult &lhs, const DtaResult &rhs) { return lhs.size < rhs.size; }
00637         static bool compareDate(const DtaResult &lhs, const DtaResult &rhs) { return lhs.date < rhs.date || (lhs.date == rhs.date && lhs.time < rhs.time); }
00638 
00639         const char * getExtension() const {
00640                 const char * ext = empty_string;
00641                 if (name[0] != '.') {
00642                         ext = strrchr(name, '.');
00643                         if (!ext) ext = empty_string;
00644                 }
00645                 return ext;
00646         }
00647 
00648 };
00649 
00650 
00651 void DOS_Shell::CMD_DIR(char * args) {
00652         HELP("DIR");
00653         char numformat[16];
00654         char path[DOS_PATHLENGTH];
00655 
00656         std::string line;
00657         if(GetEnvStr("DIRCMD",line)){
00658                 std::string::size_type idx = line.find('=');
00659                 std::string value=line.substr(idx +1 , std::string::npos);
00660                 line = std::string(args) + " " + value;
00661                 args=const_cast<char*>(line.c_str());
00662         }
00663 
00664         bool optW=ScanCMDBool(args,"W");
00665         ScanCMDBool(args,"S");
00666         bool optP=ScanCMDBool(args,"P");
00667         if (ScanCMDBool(args,"WP") || ScanCMDBool(args,"PW")) {
00668                 optW=optP=true;
00669         }
00670         bool optB=ScanCMDBool(args,"B");
00671         bool optAD=ScanCMDBool(args,"AD");
00672         bool optAminusD=ScanCMDBool(args,"A-D");
00673         // Sorting flags
00674         bool reverseSort = false;
00675         bool optON=ScanCMDBool(args,"ON");
00676         if (ScanCMDBool(args,"O-N")) {
00677                 optON = true;
00678                 reverseSort = true;
00679         }
00680         bool optOD=ScanCMDBool(args,"OD");
00681         if (ScanCMDBool(args,"O-D")) {
00682                 optOD = true;
00683                 reverseSort = true;
00684         }
00685         bool optOE=ScanCMDBool(args,"OE");
00686         if (ScanCMDBool(args,"O-E")) {
00687                 optOE = true;
00688                 reverseSort = true;
00689         }
00690         bool optOS=ScanCMDBool(args,"OS");
00691         if (ScanCMDBool(args,"O-S")) {
00692                 optOS = true;
00693                 reverseSort = true;
00694         }
00695         char * rem=ScanCMDRemain(args);
00696         if (rem) {
00697                 WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
00698                 return;
00699         }
00700         Bit32u byte_count,file_count,dir_count;
00701         Bitu w_count=0;
00702         Bitu p_count=0;
00703         Bitu w_size = optW?5:1;
00704         byte_count=file_count=dir_count=0;
00705 
00706         char buffer[CROSS_LEN];
00707         args = trim(args);
00708         size_t argLen = strlen(args);
00709         if (argLen == 0) {
00710                 strcpy(args,"*.*"); //no arguments.
00711         } else {
00712                 switch (args[argLen-1])
00713                 {
00714                 case '\\':      // handle \, C:\, etc.
00715                 case ':' :      // handle C:, etc.
00716                         strcat(args,"*.*");
00717                         break;
00718                 default:
00719                         break;
00720                 }
00721         }
00722         args = ExpandDot(args,buffer);
00723 
00724         bool con_temp = false, null_temp = false;
00725 
00726         if (!strcasecmp(args,"con"))
00727                 con_temp=true;
00728         else if (!strcasecmp(args,"nul"))
00729                 null_temp=true;
00730 
00731         if (DOS_FindDevice(args) != DOS_DEVICES) {
00732                 WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args);
00733                 return;
00734         }
00735         if (!strrchr(args,'*') && !strrchr(args,'?')) {
00736                 Bit16u attribute=0;
00737                 if(DOS_GetFileAttr(args,&attribute) && (attribute&DOS_ATTR_DIRECTORY) ) {
00738                         strcat(args,"\\*.*");   // if no wildcard and a directory, get its files
00739                 }
00740         }
00741         if (!strrchr(args,'.')) {
00742                 strcat(args,".*");      // if no extension, get them all
00743         }
00744 
00745         /* Make a full path in the args */
00746         if (!DOS_Canonicalize(args,path)) {
00747                 WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
00748                 return;
00749         }
00750         *(strrchr(path,'\\')+1)=0;
00751         if (!optB) {
00752                 CMD_VOL(empty_string);
00753                 WriteOut(MSG_Get("SHELL_CMD_DIR_INTRO"),path);
00754         }
00755 
00756         /* Command uses dta so set it to our internal dta */
00757         RealPt save_dta=dos.dta();
00758         dos.dta(dos.tables.tempdta);
00759         DOS_DTA dta(dos.dta());
00760         bool ret=DOS_FindFirst(args,0xffff & ~DOS_ATTR_VOLUME);
00761         if (!ret) {
00762                 if (!optB) WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args);
00763                 dos.dta(save_dta);
00764                 return;
00765         }
00766  
00767         if(con_temp) {
00768                 WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),"con.*");
00769                 return;
00770         } else if (null_temp) {
00771                 WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),"nul.*");
00772                 return;
00773         }
00774 
00775         std::vector<DtaResult> results;
00776 
00777         do {    /* File name and extension */
00778                 DtaResult result;
00779                 dta.GetResult(result.name,result.size,result.date,result.time,result.attr);
00780 
00781                 /* Skip non-directories if option AD is present, or skip dirs in case of A-D */
00782                 if(optAD && !(result.attr&DOS_ATTR_DIRECTORY) ) continue;
00783                 else if(optAminusD && (result.attr&DOS_ATTR_DIRECTORY) ) continue;
00784 
00785                 results.push_back(result);
00786 
00787         } while ( (ret=DOS_FindNext()) );
00788 
00789         if (optON) {
00790                 // Sort by name
00791                 std::sort(results.begin(), results.end(), DtaResult::compareName);
00792         } else if (optOE) {
00793                 // Sort by extension
00794                 std::sort(results.begin(), results.end(), DtaResult::compareExt);
00795         } else if (optOD) {
00796                 // Sort by date
00797                 std::sort(results.begin(), results.end(), DtaResult::compareDate);
00798         } else if (optOS) {
00799                 // Sort by size
00800                 std::sort(results.begin(), results.end(), DtaResult::compareSize);
00801         }
00802         if (reverseSort) {
00803                 std::reverse(results.begin(), results.end());
00804         }
00805 
00806         for (std::vector<DtaResult>::iterator iter = results.begin(); iter != results.end(); iter++) {
00807 
00808                 char * name = iter->name;
00809                 Bit32u size = iter->size;
00810                 Bit16u date = iter->date;
00811                 Bit16u time = iter->time;
00812                 Bit8u attr = iter->attr;
00813 
00814                 /* output the file */
00815                 if (optB) {
00816                         // this overrides pretty much everything
00817                         if (strcmp(".",name) && strcmp("..",name)) {
00818                                 WriteOut("%s\n",name);
00819                         }
00820                 } else {
00821                         char * ext = empty_string;
00822                         if (!optW && (name[0] != '.')) {
00823                                 ext = strrchr(name, '.');
00824                                 if (!ext) ext = empty_string;
00825                                 else *ext++ = 0;
00826                         }
00827                         Bit8u day       = (Bit8u)(date & 0x001f);
00828                         Bit8u month     = (Bit8u)((date >> 5) & 0x000f);
00829                         Bit16u year = (Bit16u)((date >> 9) + 1980);
00830                         Bit8u hour      = (Bit8u)((time >> 5 ) >> 6);
00831                         Bit8u minute = (Bit8u)((time >> 5) & 0x003f);
00832 
00833                         if (attr & DOS_ATTR_DIRECTORY) {
00834                                 if (optW) {
00835                                         WriteOut("[%s]",name);
00836                                         size_t namelen = strlen(name);
00837                                         if (namelen <= 14) {
00838                                                 for (size_t i=14-namelen;i>0;i--) WriteOut(" ");
00839                                         }
00840                                 } else {
00841                                         WriteOut("%-8s %-3s   %-16s %02d-%02d-%04d %2d:%02d\n",name,ext,"<DIR>",day,month,year,hour,minute);
00842                                 }
00843                                 dir_count++;
00844                         } else {
00845                                 if (optW) {
00846                                         WriteOut("%-16s",name);
00847                                 } else {
00848                                         FormatNumber(size,numformat);
00849                                         WriteOut("%-8s %-3s   %16s %02d-%02d-%04d %2d:%02d\n",name,ext,numformat,day,month,year,hour,minute);
00850                                 }
00851                                 file_count++;
00852                                 byte_count+=size;
00853                         }
00854                         if (optW) {
00855                                 w_count++;
00856                         }
00857                 }
00858                 if (optP && !(++p_count%(22*w_size))) {
00859                         CMD_PAUSE(empty_string);
00860                 }
00861         }
00862 
00863 
00864         if (optW) {
00865                 if (w_count%5)  WriteOut("\n");
00866         }
00867         if (!optB) {
00868                 /* Show the summary of results */
00869                 FormatNumber(byte_count,numformat);
00870                 WriteOut(MSG_Get("SHELL_CMD_DIR_BYTES_USED"),file_count,numformat);
00871                 Bit8u drive=dta.GetSearchDrive();
00872                 //TODO Free Space
00873                 Bitu free_space=1024u*1024u*100u;
00874                 if (Drives[drive]) {
00875                         Bit16u bytes_sector;Bit8u sectors_cluster;Bit16u total_clusters;Bit16u free_clusters;
00876                         Drives[drive]->AllocationInfo(&bytes_sector,&sectors_cluster,&total_clusters,&free_clusters);
00877                         free_space=(Bitu)bytes_sector * (Bitu)sectors_cluster * (Bitu)free_clusters;
00878                 }
00879                 FormatNumber(free_space,numformat);
00880                 WriteOut(MSG_Get("SHELL_CMD_DIR_BYTES_FREE"),dir_count,numformat);
00881         }
00882         dos.dta(save_dta);
00883 }
00884 
00885 struct copysource {
00886         std::string filename;
00887         bool concat;
00888         copysource(std::string filein,bool concatin):
00889                 filename(filein),concat(concatin){ };
00890         copysource():filename(""),concat(false){ };
00891 };
00892 
00893 
00894 void DOS_Shell::CMD_COPY(char * args) {
00895         extern Bitu ZDRIVE_NUM;
00896         const char root[4] = {(char)('A'+ZDRIVE_NUM),':','\\',0};
00897         char cmd[20];
00898         strcpy(cmd,root);
00899         strcat(cmd,"COPY.EXE");
00900         if (DOS_FindFirst(cmd,0xffff & ~DOS_ATTR_VOLUME)) {
00901                 StripSpaces(args);
00902                 while(ScanCMDBool(args,"T")) ; //Shouldn't this be A ?
00903                 ScanCMDBool(args,"Y");
00904                 ScanCMDBool(args,"-Y");
00905                 Execute(cmd,args);
00906                 return;
00907         }
00908 
00909         HELP("COPY");
00910         static char defaulttarget[] = ".";
00911         StripSpaces(args);
00912         /* Command uses dta so set it to our internal dta */
00913         RealPt save_dta=dos.dta();
00914         dos.dta(dos.tables.tempdta);
00915         DOS_DTA dta(dos.dta());
00916         Bit32u size;Bit16u date;Bit16u time;Bit8u attr;
00917         char name[DOS_NAMELENGTH_ASCII];
00918         std::vector<copysource> sources;
00919         // ignore /b and /t switches: always copy binary
00920         while(ScanCMDBool(args,"B")) ;
00921         while(ScanCMDBool(args,"T")) ; //Shouldn't this be A ?
00922         while(ScanCMDBool(args,"A")) ;
00923         ScanCMDBool(args,"Y");
00924         ScanCMDBool(args,"-Y");
00925         ScanCMDBool(args,"V");
00926 
00927         char * rem=ScanCMDRemain(args);
00928         if (rem) {
00929                 WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
00930                 dos.dta(save_dta);
00931                 return;
00932         }
00933         // Gather all sources (extension to copy more then 1 file specified at command line)
00934         // Concatenating files go as follows: All parts except for the last bear the concat flag.
00935         // This construction allows them to be counted (only the non concat set)
00936         char* source_p = NULL;
00937         char source_x[DOS_PATHLENGTH+CROSS_LEN];
00938         while ( (source_p = StripWord(args)) && *source_p ) {
00939                 do {
00940                         char* plus = strchr(source_p,'+');
00941                         // If StripWord() previously cut at a space before a plus then
00942                         // set concatenate flag on last source and remove leading plus.
00943                         if (plus == source_p && sources.size()) {
00944                                 sources[sources.size()-1].concat = true;
00945                                 // If spaces also followed plus then item is only a plus.
00946                                 if (strlen(++source_p)==0) break;
00947                                 plus = strchr(source_p,'+');
00948                         }
00949                         if (plus) *plus++ = 0;
00950                         safe_strncpy(source_x,source_p,CROSS_LEN);
00951                         bool has_drive_spec = false;
00952                         size_t source_x_len = strlen(source_x);
00953                         if (source_x_len>0) {
00954                                 if (source_x[source_x_len-1]==':') has_drive_spec = true;
00955                         }
00956                         if (!has_drive_spec  && !strpbrk(source_p,"*?") ) { //doubt that fu*\*.* is valid
00957                                 if (DOS_FindFirst(source_p,0xffff & ~DOS_ATTR_VOLUME)) {
00958                                         dta.GetResult(name,size,date,time,attr);
00959                                         if (attr & DOS_ATTR_DIRECTORY)
00960                                                 strcat(source_x,"\\*.*");
00961                                 }
00962                         }
00963                         sources.push_back(copysource(source_x,(plus)?true:false));
00964                         source_p = plus;
00965                 } while(source_p && *source_p);
00966         }
00967         // At least one source has to be there
00968         if (!sources.size() || !sources[0].filename.size()) {
00969                 WriteOut(MSG_Get("SHELL_MISSING_PARAMETER"));
00970                 dos.dta(save_dta);
00971                 return;
00972         };
00973 
00974         copysource target;
00975         // If more then one object exists and last target is not part of a 
00976         // concat sequence then make it the target.
00977         if(sources.size()>1 && !sources[sources.size()-2].concat){
00978                 target = sources.back();
00979                 sources.pop_back();
00980         }
00981         //If no target => default target with concat flag true to detect a+b+c
00982         if(target.filename.size() == 0) target = copysource(defaulttarget,true);
00983 
00984         copysource oldsource;
00985         copysource source;
00986         Bit32u count = 0;
00987         while(sources.size()) {
00988                 /* Get next source item and keep track of old source for concat start end */
00989                 oldsource = source;
00990                 source = sources[0];
00991                 sources.erase(sources.begin());
00992 
00993                 //Skip first file if doing a+b+c. Set target to first file
00994                 if(!oldsource.concat && source.concat && target.concat) {
00995                         target = source;
00996                         continue;
00997                 }
00998 
00999                 /* Make a full path in the args */
01000                 char pathSource[DOS_PATHLENGTH];
01001                 char pathTarget[DOS_PATHLENGTH];
01002 
01003                 if (!DOS_Canonicalize(const_cast<char*>(source.filename.c_str()),pathSource)) {
01004                         WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
01005                         dos.dta(save_dta);
01006                         return;
01007                 }
01008                 // cut search pattern
01009                 char* pos = strrchr(pathSource,'\\');
01010                 if (pos) *(pos+1) = 0;
01011 
01012                 if (!DOS_Canonicalize(const_cast<char*>(target.filename.c_str()),pathTarget)) {
01013                         WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
01014                         dos.dta(save_dta);
01015                         return;
01016                 }
01017                 char* temp = strstr(pathTarget,"*.*");
01018                 if(temp && (temp == pathTarget || temp[-1] == '\\')) *temp = 0;//strip off *.* from target
01019         
01020                 // add '\\' if target is a directory
01021                 if (pathTarget[strlen(pathTarget)-1]!='\\') {
01022                         if (DOS_FindFirst(pathTarget,0xffff & ~DOS_ATTR_VOLUME)) {
01023                                 dta.GetResult(name,size,date,time,attr);
01024                                 if (attr & DOS_ATTR_DIRECTORY)  
01025                                         strcat(pathTarget,"\\");
01026                         }
01027                 };
01028 
01029                 //Find first sourcefile
01030                 bool ret = DOS_FindFirst(const_cast<char*>(source.filename.c_str()),0xffff & ~DOS_ATTR_VOLUME);
01031                 if (!ret) {
01032                         WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),const_cast<char*>(source.filename.c_str()));
01033                         dos.dta(save_dta);
01034                         return;
01035                 }
01036 
01037                 Bit16u sourceHandle,targetHandle;
01038                 char nameTarget[DOS_PATHLENGTH];
01039                 char nameSource[DOS_PATHLENGTH];
01040                 
01041                 // Cache so we don't have to recalculate
01042                 size_t pathTargetLen = strlen(pathTarget);
01043                 
01044                 // See if we have to substitute filename or extension
01045                 char *ext = 0;
01046                 size_t replacementOffset = 0;
01047                 if (pathTarget[pathTargetLen-1]!='\\') { 
01048                                 // only if it's not a directory
01049                         ext = strchr(pathTarget, '.');
01050                         if (ext > pathTarget) { // no possible substitution
01051                                 if (ext[-1] == '*') {
01052                                         // we substitute extension, save it, hide the name
01053                                         ext[-1] = 0;
01054                                         assert(ext > pathTarget + 1); // pathTarget is fully qualified
01055                                         if (ext[-2] != '\\') {
01056                                                 // there is something before the asterisk
01057                                                 // save the offset in the source names
01058                                                 replacementOffset = source.filename.find('*');
01059                                                 size_t lastSlash = source.filename.rfind('\\');
01060                                                 if (std::string::npos == lastSlash)
01061                                                         lastSlash = 0;
01062                                                 else
01063                                                         lastSlash++;
01064                                                 if (std::string::npos == replacementOffset
01065                                                           || replacementOffset < lastSlash) {
01066                                                         // no asterisk found or in wrong place, error
01067                                                         WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
01068                                                         dos.dta(save_dta);
01069                                                         return;
01070                                                 }
01071                                                 replacementOffset -= lastSlash;
01072 //                                              WriteOut("[II] replacement offset is %d\n", replacementOffset);
01073                                         }
01074                                 }
01075                                 if (ext[1] == '*') {
01076                                         // we substitute name, clear the extension
01077                                         *ext = 0;
01078                                 } else if (ext[-1]) {
01079                                         // we don't substitute anything, clear up
01080                                         ext = 0;
01081                                 }
01082                         }
01083                 }
01084 
01085                 while (ret) {
01086                         dta.GetResult(name,size,date,time,attr);
01087 
01088                         if ((attr & DOS_ATTR_DIRECTORY)==0) {
01089                                 strcpy(nameSource,pathSource);
01090                                 strcat(nameSource,name);
01091                                 // Open Source
01092                                 if (DOS_OpenFile(nameSource,0,&sourceHandle)) {
01093                                         // Create Target or open it if in concat mode
01094                                         strcpy(nameTarget,pathTarget);
01095                                         
01096                                         if (ext) { // substitute parts if necessary
01097                                                 if (!ext[-1]) { // substitute extension
01098                                                         strcat(nameTarget, name + replacementOffset);
01099                                                         strcpy(strchr(nameTarget, '.'), ext);
01100                                                 }
01101                                                 if (ext[1] == '*') { // substitute name (so just add the extension)
01102                                                         strcat(nameTarget, strchr(name, '.'));
01103                                                 }
01104                                         }
01105                                         
01106                                         if (nameTarget[pathTargetLen-1]=='\\') strcat(nameTarget,name);
01107                                         //Don't create a new file when in concat mode
01108                                         if (oldsource.concat || DOS_CreateFile(nameTarget,0,&targetHandle)) {
01109                                                 Bit32u dummy=0;
01110                                                 //In concat mode. Open the target and seek to the eof
01111                                                 if (!oldsource.concat || (DOS_OpenFile(nameTarget,OPEN_READWRITE,&targetHandle) && 
01112                                                                           DOS_SeekFile(targetHandle,&dummy,DOS_SEEK_END))) {
01113                                                         // Copy 
01114                                                         static Bit8u buffer[0x8000]; // static, otherwise stack overflow possible.
01115                                                         bool    failed = false;
01116                                                         Bit16u  toread = 0x8000;
01117                                                         do {
01118                                                                 failed |= DOS_ReadFile(sourceHandle,buffer,&toread);
01119                                                                 failed |= DOS_WriteFile(targetHandle,buffer,&toread);
01120                                                         } while (toread==0x8000);
01121                                                         failed |= DOS_CloseFile(sourceHandle);
01122                                                         failed |= DOS_CloseFile(targetHandle);
01123                                                         WriteOut(" %s\n",name);
01124                                                         if(!source.concat) count++; //Only count concat files once
01125                                                 } else {
01126                                                         DOS_CloseFile(sourceHandle);
01127                                                         WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast<char*>(target.filename.c_str()));
01128                                                 }
01129                                         } else {
01130                                                 DOS_CloseFile(sourceHandle);
01131                                                 WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast<char*>(target.filename.c_str()));
01132                                         }
01133                                 } else WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast<char*>(source.filename.c_str()));
01134                         };
01135                         //On to the next file if the previous one wasn't a device
01136                         if ((attr&DOS_ATTR_DEVICE) == 0) ret = DOS_FindNext();
01137                         else ret = false;
01138                 };
01139         }
01140 
01141         WriteOut(MSG_Get("SHELL_CMD_COPY_SUCCESS"),count);
01142         dos.dta(save_dta);
01143 }
01144 
01145 /* NTS: WARNING, this function modifies the buffer pointed to by char *args */
01146 void DOS_Shell::CMD_SET(char * args) {
01147         std::string line;
01148 
01149         HELP("SET");
01150         StripSpaces(args);
01151 
01152         if (*args == 0) { /* "SET" by itself means to show the environment block */
01153                 Bitu count = GetEnvCount();
01154 
01155                 for (Bitu a = 0;a < count;a++) {
01156                         if (GetEnvNum(a,line))
01157                                 WriteOut("%s\n",line.c_str());                  
01158                 }
01159 
01160         }
01161         else {
01162                 char *p;
01163 
01164                 { /* parse arguments at the start */
01165                         char *pcheck = args;
01166 
01167                         while (*pcheck != 0 && (*pcheck == ' ' || *pcheck == '\t')) pcheck++;
01168                         if (*pcheck != 0 && strlen(pcheck) > 3 && (strncasecmp(pcheck,"/p ",3) == 0))
01169                                 E_Exit("Set /P is not supported. Use Choice!"); /* TODO: What is SET /P supposed to do? */
01170                 }
01171 
01172                 /* Most SET commands take the form NAME=VALUE */
01173                 p = strchr(args,'=');
01174                 if (p == NULL) {
01175                         /* SET <variable> without assignment prints the variable instead */
01176                         if (!GetEnvStr(args,line)) WriteOut(MSG_Get("SHELL_CMD_SET_NOT_SET"),args);
01177                         WriteOut("%s\n",line.c_str());
01178                 } else {
01179                         /* ASCIIZ snip the args string in two, so that args is C-string name of the variable,
01180                          * and "p" is C-string value of the variable */
01181                         *p++ = 0;
01182 
01183                         /* No parsing is needed. The command interpreter does the variable substitution for us */
01184                         if (!SetEnv(args,p)) {
01185                                 /* NTS: If Win95 is any example, the command interpreter expands the variables for us */
01186                                 WriteOut(MSG_Get("SHELL_CMD_SET_OUT_OF_SPACE"));
01187                         }
01188                 }
01189         }
01190 }
01191 
01192 void DOS_Shell::CMD_IF(char * args) {
01193         HELP("IF");
01194         StripSpaces(args,'=');
01195         bool has_not=false;
01196 
01197         while (strncasecmp(args,"NOT",3) == 0) {
01198                 if (!isspace(*reinterpret_cast<unsigned char*>(&args[3])) && (args[3] != '=')) break;
01199                 args += 3;      //skip text
01200                 //skip more spaces
01201                 StripSpaces(args,'=');
01202                 has_not = !has_not;
01203         }
01204 
01205         if(strncasecmp(args,"ERRORLEVEL",10) == 0) {
01206                 args += 10;     //skip text
01207                 //Strip spaces and ==
01208                 StripSpaces(args,'=');
01209                 char* word = StripWord(args);
01210                 if(!isdigit(*word)) {
01211                         WriteOut(MSG_Get("SHELL_CMD_IF_ERRORLEVEL_MISSING_NUMBER"));
01212                         return;
01213                 }
01214 
01215                 Bit8u n = 0;
01216                 do n = n * 10 + (*word - '0');
01217                 while (isdigit(*++word));
01218                 if(*word && !isspace(*word)) {
01219                         WriteOut(MSG_Get("SHELL_CMD_IF_ERRORLEVEL_INVALID_NUMBER"));
01220                         return;
01221                 }
01222                 /* Read the error code from DOS */
01223                 if ((dos.return_code>=n) ==(!has_not)) DoCommand(args);
01224                 return;
01225         }
01226 
01227         if(strncasecmp(args,"EXIST ",6) == 0) {
01228                 args += 6; //Skip text
01229                 StripSpaces(args);
01230                 char* word = StripWord(args);
01231                 if (!*word) {
01232                         WriteOut(MSG_Get("SHELL_CMD_IF_EXIST_MISSING_FILENAME"));
01233                         return;
01234                 }
01235 
01236                 {       /* DOS_FindFirst uses dta so set it to our internal dta */
01237                         RealPt save_dta=dos.dta();
01238                         dos.dta(dos.tables.tempdta);
01239                         bool ret=DOS_FindFirst(word,0xffff & ~DOS_ATTR_VOLUME);
01240                         dos.dta(save_dta);
01241                         if (ret==(!has_not)) DoCommand(args);
01242                 }
01243                 return;
01244         }
01245 
01246         /* Normal if string compare */
01247 
01248         char* word1 = args;
01249         // first word is until space or =
01250         while (*args && !isspace(*reinterpret_cast<unsigned char*>(args)) && (*args != '='))
01251                 args++;
01252         char* end_word1 = args;
01253 
01254         // scan for =
01255         while (*args && (*args != '='))
01256                 args++;
01257         // check for ==
01258         if ((*args==0) || (args[1] != '=')) {
01259                 SyntaxError();
01260                 return;
01261         }
01262         args += 2;
01263         StripSpaces(args,'=');
01264 
01265         char* word2 = args;
01266         // second word is until space or =
01267         while (*args && !isspace(*reinterpret_cast<unsigned char*>(args)) && (*args != '='))
01268                 args++;
01269 
01270         if (*args) {
01271                 *end_word1 = 0;         // mark end of first word
01272                 *args++ = 0;            // mark end of second word
01273                 StripSpaces(args,'=');
01274 
01275                 if ((strcmp(word1,word2)==0)==(!has_not)) DoCommand(args);
01276         }
01277 }
01278 
01279 void DOS_Shell::CMD_GOTO(char * args) {
01280         HELP("GOTO");
01281         StripSpaces(args);
01282         if (!bf) return;
01283         if (*args &&(*args==':')) args++;
01284         //label ends at the first space
01285         char* non_space = args;
01286         while (*non_space) {
01287                 if((*non_space == ' ') || (*non_space == '\t')) 
01288                         *non_space = 0; 
01289                 else non_space++;
01290         }
01291         if (!*args) {
01292                 WriteOut(MSG_Get("SHELL_CMD_GOTO_MISSING_LABEL"));
01293                 return;
01294         }
01295         if (!bf->Goto(args)) {
01296                 WriteOut(MSG_Get("SHELL_CMD_GOTO_LABEL_NOT_FOUND"),args);
01297                 return;
01298         }
01299 }
01300 
01301 void DOS_Shell::CMD_SHIFT(char * args ) {
01302         HELP("SHIFT");
01303         if(bf) bf->Shift();
01304 }
01305 
01306 void DOS_Shell::CMD_TYPE(char * args) {
01307         HELP("TYPE");
01308                 
01309         // ignore /p /h and /t for compatibility
01310         ScanCMDBool(args,"P");
01311         ScanCMDBool(args,"H");
01312         ScanCMDBool(args,"T");
01313         StripSpaces(args);
01314         if (strcasecmp(args,"nul")==0) return;
01315         if (!*args) {
01316                 WriteOut(MSG_Get("SHELL_SYNTAXERROR"));
01317                 return;
01318         }
01319         Bit16u handle;
01320         char * word;
01321 nextfile:
01322         word=StripWord(args);
01323         if (!DOS_OpenFile(word,0,&handle)) {
01324                 WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),word);
01325                 return;
01326         }
01327         Bit8u c;Bit16u n=1;
01328         while (n) {
01329                 DOS_ReadFile(handle,&c,&n);
01330                 if (n==0 || c==0x1a) break; // stop at EOF
01331                 DOS_WriteFile(STDOUT,&c,&n);
01332         }
01333         DOS_CloseFile(handle);
01334         if (*args) goto nextfile;
01335 }
01336 
01337 void DOS_Shell::CMD_REM(char * args) {
01338         HELP("REM");
01339 }
01340 
01341 static void PAUSED(void) {
01342         Bit8u c; Bit16u n=1;
01343         DOS_ReadFile (STDIN,&c,&n);
01344 }
01345 
01346 void DOS_Shell::CMD_MORE(char * args) {
01347         HELP("MORE");
01348         //ScanCMDBool(args,">");
01349         if(!*args) {
01350                 char defaultcon[DOS_PATHLENGTH+CROSS_LEN+20]={ 0 };
01351                 strcpy(defaultcon,"copy con >nul");
01352                 this->ParseLine(defaultcon);
01353                 return;
01354         }
01355         int nchars = 0, nlines = 0, linecount = 0, LINES = (Bit16u)mem_readb(BIOS_ROWS_ON_SCREEN_MINUS_1)-3, COLS = mem_readw(BIOS_SCREEN_COLUMNS), TABSIZE = 8;
01356         StripSpaces(args);
01357         if (strcasecmp(args,"nul")==0) return;
01358         if (!*args) {
01359                 WriteOut(MSG_Get("SHELL_SYNTAXERROR"));
01360                 return;
01361         }
01362         Bit16u handle;
01363         char * word;
01364 nextfile:
01365         word=StripWord(args);
01366         if (!DOS_OpenFile(word,0,&handle)) {
01367                 WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),word);
01368                 return;
01369         }
01370         Bit16u n; Bit8u c;
01371         do {
01372                 n=1;
01373                 DOS_ReadFile(handle,&c,&n);
01374                 DOS_WriteFile(STDOUT,&c,&n);
01375                 if (c != '\t') nchars++;
01376                 else do {
01377                         WriteOut(" ");
01378                         nchars++;
01379                 } while ( nchars < COLS && nchars % TABSIZE );
01380 
01381                 if (c == '\n') linecount++;
01382                 if ((c == '\n') || (nchars >= COLS)) {
01383                         nlines++;
01384                         nchars = 0;
01385                         if (nlines == (LINES-1)) {
01386                                 WriteOut("\n-- More -- %s (%u) --\n",word,linecount);
01387                                 PAUSED();
01388                                 nlines=0;
01389                         }
01390                 }
01391         } while (n);
01392         DOS_CloseFile(handle);
01393         if (*args) {
01394                 WriteOut("\n");
01395                 PAUSED();
01396                 goto nextfile;
01397         }
01398 }
01399 
01400 void DOS_Shell::CMD_PAUSE(char * args){
01401         HELP("PAUSE");
01402         if(args && *args) {
01403                 args++;
01404                 WriteOut("%s\n",args);  // optional specified message
01405         } else
01406         WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
01407         Bit8u c;Bit16u n=1;
01408         DOS_ReadFile(STDIN,&c,&n);
01409         if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
01410 }
01411 
01412 void DOS_Shell::CMD_CALL(char * args){
01413         HELP("CALL");
01414         this->call=true; /* else the old batchfile will be closed first */
01415         this->ParseLine(args);
01416         this->call=false;
01417 }
01418 
01419 extern bool date_host_forced;
01420 void DOS_Shell::CMD_DATE(char * args) {
01421         HELP("DATE");   
01422         if(ScanCMDBool(args,"H")) {
01423                 // synchronize date with host parameter
01424                 time_t curtime;
01425                 struct tm *loctime;
01426                 curtime = time (NULL);
01427                 loctime = localtime (&curtime);
01428                 
01429                 reg_cx = loctime->tm_year+1900;
01430                 reg_dh = loctime->tm_mon+1;
01431                 reg_dl = loctime->tm_mday;
01432 
01433                 reg_ah=0x2b; // set system date
01434                 CALLBACK_RunRealInt(0x21);
01435                 return;
01436         }
01437         // check if a date was passed in command line
01438         Bit32u newday,newmonth,newyear;
01439         if(sscanf(args,"%u-%u-%u",&newmonth,&newday,&newyear)==3) {
01440                 reg_cx = static_cast<Bit16u>(newyear);
01441                 reg_dh = static_cast<Bit8u>(newmonth);
01442                 reg_dl = static_cast<Bit8u>(newday);
01443 
01444                 reg_ah=0x2b; // set system date
01445                 CALLBACK_RunRealInt(0x21);
01446                 if(reg_al==0xff) WriteOut(MSG_Get("SHELL_CMD_DATE_ERROR"));
01447                 return;
01448         }
01449         // display the current date
01450         reg_ah=0x2a; // get system date
01451         CALLBACK_RunRealInt(0x21);
01452 
01453         const char* datestring = MSG_Get("SHELL_CMD_DATE_DAYS");
01454         Bit32u length;
01455         char day[6] = {0};
01456         if(sscanf(datestring,"%u",&length) && (length<5) && (strlen(datestring)==(length*7+1))) {
01457                 // date string appears valid
01458                 for(Bit32u i = 0; i < length; i++) day[i] = datestring[reg_al*length+1+i];
01459         }
01460         bool dateonly = ScanCMDBool(args,"T");
01461         if(!dateonly) WriteOut(MSG_Get("SHELL_CMD_DATE_NOW"));
01462 
01463         const char* formatstring = MSG_Get("SHELL_CMD_DATE_FORMAT");
01464         if(strlen(formatstring)!=5) return;
01465         char buffer[15] = {0};
01466         Bitu bufferptr=0;
01467         for(Bitu i = 0; i < 5; i++) {
01468                 if(i==1 || i==3) {
01469                         buffer[bufferptr] = formatstring[i];
01470                         bufferptr++;
01471                 } else {
01472                         if(formatstring[i]=='M') bufferptr += (Bitu)sprintf(buffer+bufferptr,"%02u",(Bit8u) reg_dh);
01473                         if(formatstring[i]=='D') bufferptr += (Bitu)sprintf(buffer+bufferptr,"%02u",(Bit8u) reg_dl);
01474                         if(formatstring[i]=='Y') bufferptr += (Bitu)sprintf(buffer+bufferptr,"%04u",(Bit16u) reg_cx);
01475                 }
01476         }
01477         if(date_host_forced) {
01478                 time_t curtime;
01479 
01480                 struct tm *loctime;
01481                 curtime = time (NULL);
01482 
01483                 loctime = localtime (&curtime);
01484                 int hosty=loctime->tm_year+1900;
01485                 int hostm=loctime->tm_mon+1;
01486                 int hostd=loctime->tm_mday;
01487                 if (hostm == 1 || hostm == 2) hosty--;
01488                 hostm = (hostm + 9) % 12 + 1;
01489                 int y = hosty % 100;
01490                 int century = hosty / 100;
01491                 int week = ((13 * hostm - 1) / 5 + hostd + y + y/4 + century/4 - 2*century) % 7;
01492                 if (week < 0) week = (week + 7) % 7;
01493 
01494                 const char* my_week[7]={"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
01495                 WriteOut("%s %s\n",my_week[week],buffer);
01496         } else
01497         WriteOut("%s %s\n",day, buffer);
01498         if(!dateonly) WriteOut(MSG_Get("SHELL_CMD_DATE_SETHLP"));
01499 }
01500 
01501 void DOS_Shell::CMD_TIME(char * args) {
01502         HELP("TIME");
01503         if(ScanCMDBool(args,"H")) {
01504                 // synchronize time with host parameter
01505                 time_t curtime;
01506                 struct tm *loctime;
01507                 curtime = time (NULL);
01508                 loctime = localtime (&curtime);
01509                 
01510                 //reg_cx = loctime->;
01511                 //reg_dh = loctime->;
01512                 //reg_dl = loctime->;
01513 
01514                 // reg_ah=0x2d; // set system time TODO
01515                 // CALLBACK_RunRealInt(0x21);
01516                 
01517                 Bit32u ticks=(Bit32u)(((double)(loctime->tm_hour*3600+
01518                                                                                 loctime->tm_min*60+
01519                                                                                 loctime->tm_sec))*18.206481481);
01520                 mem_writed(BIOS_TIMER,ticks);
01521                 return;
01522         }
01523         bool timeonly = ScanCMDBool(args,"T");
01524 
01525         reg_ah=0x2c; // get system time
01526         CALLBACK_RunRealInt(0x21);
01527 /*
01528                 reg_dl= // 1/100 seconds
01529                 reg_dh= // seconds
01530                 reg_cl= // minutes
01531                 reg_ch= // hours
01532 */
01533         if(timeonly) {
01534                 WriteOut("%2u:%02u\n",reg_ch,reg_cl);
01535         } else {
01536                 WriteOut(MSG_Get("SHELL_CMD_TIME_NOW"));
01537                 WriteOut("%2u:%02u:%02u,%02u\n",reg_ch,reg_cl,reg_dh,reg_dl);
01538         }
01539 }
01540 
01541 void DOS_Shell::CMD_SUBST (char * args) {
01542 /* If more that one type can be substed think of something else 
01543  * E.g. make basedir member dos_drive instead of localdrive
01544  */
01545         HELP("SUBST");
01546         localDrive* ldp=0;
01547         char mountstring[DOS_PATHLENGTH+CROSS_LEN+20];
01548         char temp_str[2] = { 0,0 };
01549         try {
01550                 strcpy(mountstring,"MOUNT ");
01551                 StripSpaces(args);
01552                 std::string arg;
01553                 CommandLine command(0,args);
01554 
01555                 if (command.GetCount() != 2) throw 0 ;
01556   
01557                 command.FindCommand(1,arg);
01558                 if( (arg.size()>1) && arg[1] !=':')  throw(0);
01559                 temp_str[0]=(char)toupper(args[0]);
01560                 command.FindCommand(2,arg);
01561                 if((arg=="/D") || (arg=="/d")) {
01562                         if(!Drives[temp_str[0]-'A'] ) throw 1; //targetdrive not in use
01563                         strcat(mountstring,"-u ");
01564                         strcat(mountstring,temp_str);
01565                         this->ParseLine(mountstring);
01566                         return;
01567                 }
01568                 if(Drives[temp_str[0]-'A'] ) throw 0; //targetdrive in use
01569                 strcat(mountstring,temp_str);
01570                 strcat(mountstring," ");
01571 
01572                 Bit8u drive;char fulldir[DOS_PATHLENGTH];
01573                 if (!DOS_MakeName(const_cast<char*>(arg.c_str()),fulldir,&drive)) throw 0;
01574         
01575                 if( ( ldp=dynamic_cast<localDrive*>(Drives[drive])) == 0 ) throw 0;
01576                 char newname[CROSS_LEN];   
01577                 strcpy(newname, ldp->basedir);     
01578                 strcat(newname,fulldir);
01579                 CROSS_FILENAME(newname);
01580                 ldp->dirCache.ExpandName(newname);
01581                 strcat(mountstring,"\"");          
01582                 strcat(mountstring, newname);
01583                 strcat(mountstring,"\"");          
01584                 this->ParseLine(mountstring);
01585         }
01586         catch(int a){
01587                 if(a == 0) {
01588                         WriteOut(MSG_Get("SHELL_CMD_SUBST_FAILURE"));
01589                 } else {
01590                         WriteOut(MSG_Get("SHELL_CMD_SUBST_NO_REMOVE"));
01591                 }
01592                 return;
01593         }
01594         catch(...) {            //dynamic cast failed =>so no localdrive
01595                 WriteOut(MSG_Get("SHELL_CMD_SUBST_FAILURE"));
01596                 return;
01597         }
01598    
01599         return;
01600 }
01601 
01602 void DOS_Shell::CMD_LOADHIGH(char *args){
01603         HELP("LOADHIGH");
01604         Bit16u umb_start=dos_infoblock.GetStartOfUMBChain();
01605         Bit8u umb_flag=dos_infoblock.GetUMBChainState();
01606         Bit8u old_memstrat=(Bit8u)(DOS_GetMemAllocStrategy()&0xff);
01607         if (umb_start==0x9fff) {
01608                 if ((umb_flag&1)==0) DOS_LinkUMBsToMemChain(1);
01609                 DOS_SetMemAllocStrategy(0x80);  // search in UMBs first
01610                 this->ParseLine(args);
01611                 Bit8u current_umb_flag=dos_infoblock.GetUMBChainState();
01612                 if ((current_umb_flag&1)!=(umb_flag&1)) DOS_LinkUMBsToMemChain(umb_flag);
01613                 DOS_SetMemAllocStrategy(old_memstrat);  // restore strategy
01614         } else this->ParseLine(args);
01615 }
01616 
01617 void DOS_Shell::CMD_CHOICE(char * args){
01618         HELP("CHOICE");
01619         static char defchoice[3] = {'y','n',0};
01620         char *rem = NULL, *ptr;
01621         bool optN = ScanCMDBool(args,"N");
01622         bool optS = ScanCMDBool(args,"S"); //Case-sensitive matching
01623         // ignore /b and /m switches for compatibility
01624         ScanCMDBool(args,"B");
01625         ScanCMDBool(args,"M"); // Text
01626         ScanCMDBool(args,"T"); //Default Choice after timeout
01627         if (args) {
01628                 char *last = strchr(args,0);
01629                 StripSpaces(args);
01630                 rem = ScanCMDRemain(args);
01631                 if (rem && *rem && (tolower(rem[1]) != 'c')) {
01632                         WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
01633                         return;
01634                 }
01635                 if (args == rem) args = strchr(rem,0)+1;
01636                 if (rem) rem += 2;
01637                 if(rem && rem[0]==':') rem++; /* optional : after /c */
01638                 if (args > last) args = NULL;
01639         }
01640         if (!rem || !*rem) rem = defchoice; /* No choices specified use YN */
01641         ptr = rem;
01642         Bit8u c;
01643         if(!optS) while ((c = (Bit8u)(*ptr))) *ptr++ = (char)toupper(c); /* When in no case-sensitive mode. make everything upcase */
01644         if(args && *args ) {
01645                 StripSpaces(args);
01646                 size_t argslen = strlen(args);
01647                 if(argslen>1 && args[0] == '"' && args[argslen-1] =='"') {
01648                         args[argslen-1] = 0; //Remove quotes
01649                         args++;
01650                 }
01651                 WriteOut(args);
01652         }
01653         /* Show question prompt of the form [a,b]? where a b are the choice values */
01654         if (!optN) {
01655                 if(args && *args) WriteOut(" ");
01656                 WriteOut("[");
01657                 size_t len = strlen(rem);
01658                 for(size_t t = 1; t < len; t++) {
01659                         WriteOut("%c,",rem[t-1]);
01660                 }
01661                 WriteOut("%c]?",rem[len-1]);
01662         }
01663 
01664         Bit16u n=1;
01665         do {
01666                 DOS_ReadFile (STDIN,&c,&n);
01667         } while (!c || !(ptr = strchr(rem,(optS?c:toupper(c)))));
01668         c = optS?c:(Bit8u)toupper(c);
01669         DOS_WriteFile (STDOUT,&c, &n);
01670         c = '\n'; DOS_WriteFile (STDOUT,&c, &n);
01671         dos.return_code = (Bit8u)(ptr-rem+1);
01672 }
01673 
01674 void DOS_Shell::CMD_ATTRIB(char *args){
01675         HELP("ATTRIB");
01676         // No-Op for now.
01677 }
01678 
01679 void DOS_Shell::CMD_PROMPT(char *args){
01680         HELP("PROMPT");
01681         if(args && *args && strlen(args)) {
01682                 args++;
01683                 SetEnv("PROMPT",args);
01684         } else
01685                 SetEnv("PROMPT","$P$G");
01686         return;
01687 }
01688 
01689 void DOS_Shell::CMD_LABEL(char *args){
01690         HELP("LABEL");
01691         Bit8u drive = DOS_GetDefaultDrive();
01692         if(args && *args) {
01693                 std::string label;
01694                 args++;
01695                 label = args;
01696                 Drives[drive]->SetLabel(label.c_str(),false,true);
01697                 return;
01698         }
01699         WriteOut(MSG_Get("SHELL_CMD_LABEL_HELP")); WriteOut("\n");
01700         WriteOut(MSG_Get("SHELL_CMD_LABEL_HELP_LONG"));
01701         return;
01702 }
01703 
01704 void DOS_Shell::CMD_PATH(char *args){
01705         HELP("PATH");
01706         if(args && *args && strlen(args)){
01707                 char pathstring[DOS_PATHLENGTH+CROSS_LEN+20]={ 0 };
01708                 strcpy(pathstring,"set PATH=");
01709                 while(args && *args && (*args=='='|| *args==' ')) 
01710                      args++;
01711                 strcat(pathstring,args);
01712                 this->ParseLine(pathstring);
01713                 return;
01714         } else {
01715                 std::string line;
01716                 if(GetEnvStr("PATH",line)) {
01717                         WriteOut("%s\n",line.c_str());
01718                 } else {
01719                         WriteOut("PATH=(null)\n");
01720                 }
01721         }
01722 }
01723 
01724 void DOS_Shell::CMD_VER(char *args) {
01725         HELP("VER");
01726         if(args && *args) {
01727                 char* word = StripWord(args);
01728                 if(strcasecmp(word,"set")) return;
01729                 word = StripWord(args);
01730                 dos.version.major = (Bit8u)(atoi(word));
01731                 dos.version.minor = (Bit8u)(atoi(args));
01732         } else WriteOut(MSG_Get("SHELL_CMD_VER_VER"),VERSION,dos.version.major,dos.version.minor);
01733 }
01734 
01735 void DOS_Shell::CMD_VOL(char *args){
01736         HELP("VOL");
01737         Bit8u drive=DOS_GetDefaultDrive();
01738         if(args && *args && strlen(args)){
01739                 args++;
01740                 Bit32u argLen = (Bit32u)strlen(args);
01741                 switch (args[argLen-1]) {
01742                 case ':' :
01743                         if(!strcasecmp(args,":")) return;
01744                         int drive2; drive2= toupper(*reinterpret_cast<unsigned char*>(&args[0]));
01745                         char * c; c = strchr(args,':'); *c = '\0';
01746                         if (Drives[drive2-'A']) drive = drive2 - 'A';
01747                         else {
01748                                 WriteOut(MSG_Get("SHELL_CMD_VOL_DRIVEERROR"));
01749                                 return;
01750                         }
01751                         break;
01752                 default:
01753                         return;
01754                 }
01755         }
01756         char const* bufin = Drives[drive]->GetLabel();
01757         WriteOut(MSG_Get("SHELL_CMD_VOL_DRIVE"),drive+'A');
01758 
01759         //if((drive+'A')=='Z') bufin="DOSBOX";
01760         if(strcasecmp(bufin,"")==0)
01761                 WriteOut(MSG_Get("SHELL_CMD_VOL_SERIAL_NOLABEL"));
01762         else
01763                 WriteOut(MSG_Get("SHELL_CMD_VOL_SERIAL_LABEL"),bufin);
01764 
01765         WriteOut(MSG_Get("SHELL_CMD_VOL_SERIAL"));
01766         WriteOut("0000-1234\n"); // fake serial number
01767         return;
01768 }
01769 
01770 void SetVal(const std::string secname, std::string preval, const std::string val);
01771 static void delayed_press(Bitu key) { KEYBOARD_AddKey((KBD_KEYS)key,true); }
01772 static void delayed_release(Bitu key) { KEYBOARD_AddKey((KBD_KEYS)key,false); }
01773 static void delayed_sdlpress(Bitu core) {
01774         if(core==1) SetVal("cpu","core","normal");
01775         else if(core==2) SetVal("cpu","core","simple");
01776         else if(core==3) SetVal("cpu","core","dynamic");
01777         else if(core==3) SetVal("cpu","core","full");
01778 }
01779 // ADDKEY patch was created by Moe
01780 void DOS_Shell::CMD_ADDKEY(char * args){
01781         HELP("ADDKEY");
01782         StripSpaces(args);
01783         if (!*args) {
01784                 WriteOut(MSG_Get("SHELL_SYNTAXERROR"));
01785                 return;
01786         }
01787         char * word;
01788         int delay = 0, duration = 0, core=0;
01789 
01790         while (*args) {
01791                 word=StripWord(args);
01792                 KBD_KEYS scankey = (KBD_KEYS)0;
01793                 char *tail;
01794                 bool alt = false, control = false, shift = false;
01795                 while (word[1] == '-') {
01796                         switch (word[0]) {
01797                                 case 'c':
01798                                         control = true;
01799                                         word += 2;
01800                                         break;
01801                                 case 's':
01802                                         shift = true;
01803                                         word += 2;
01804                                         break;
01805                                 case 'a':
01806                                         alt = true;
01807                                         word += 2;
01808                                         break;
01809                                 default:
01810                                         WriteOut(MSG_Get("SHELL_SYNTAXERROR"));
01811                                         return;
01812                         }
01813                 }
01814                 if (!strcasecmp(word,"enter")) {
01815                         word[0] = (char)10;
01816                         word[1] = (char)0;
01817                 } else if (!strcasecmp(word,"space")) {
01818                         word[0] = (char)32;
01819                         word[1] = (char)0;
01820                 } else if (!strcasecmp(word,"bs")) {
01821                         word[0] = (char)8;
01822                         word[1] = (char)0;
01823                 } else if (!strcasecmp(word,"tab")) {
01824                         word[0] = (char)9;
01825                         word[1] = (char)0;
01826                 } else if (!strcasecmp(word,"escape")) {
01827                         word[0] = (char)27;
01828                         word[1] = (char)0;
01829                 } else if (!strcasecmp(word,"up")) {
01830                         word[0] = (char)141;
01831                         word[1] = (char)0;
01832                 } else if (!strcasecmp(word,"down")) {
01833                         word[0] = (char)142;
01834                         word[1] = (char)0;
01835                 } else if (!strcasecmp(word,"left")) {
01836                         word[0] = (char)143;
01837                         word[1] = (char)0;
01838                 } else if (!strcasecmp(word,"right")) {
01839                         word[0] = (char)144;
01840                         word[1] = (char)0;
01841                 } else if (!strcasecmp(word,"ins")) {
01842                         word[0] = (char)145;
01843                         word[1] = (char)0;
01844                 } else if (!strcasecmp(word,"del")) {
01845                         word[0] = (char)146;
01846                         word[1] = (char)0;
01847                 } else if (!strcasecmp(word,"home")) {
01848                         word[0] = (char)147;
01849                         word[1] = (char)0;
01850                 } else if (!strcasecmp(word,"end")) {
01851                         word[0] = (char)148;
01852                         word[1] = (char)0;
01853                 } else if (!strcasecmp(word,"pgup")) {
01854                         word[0] = (char)149;
01855                         word[1] = (char)0;
01856                 } else if (!strcasecmp(word,"pgdown")) {
01857                         word[0] = (char)150;
01858                         word[1] = (char)0;
01859                 } else if (!strcasecmp(word,"normal")) {
01860                         core = 1;
01861                 } else if (!strcasecmp(word,"simple")) {
01862                         core = 2;
01863                 } else if (!strcasecmp(word,"dynamic")) {
01864                         core = 3;
01865                 } else if (!strcasecmp(word,"full")) {
01866                         core = 4;
01867                 } else if (word[0] == 'k' && word[1] == 'p' && word[2] & !word[3]) {
01868                         word[0] = 151+word[2]-'0';
01869                         word[1] = 0;
01870                 } else if (word[0] == 'f' && word[1]) {
01871                         word[0] = 128+word[1]-'0';
01872                         if (word[1] == '1' && word[2]) word[0] = 128+word[2]-'0'+10;
01873                         word[1] = 0;
01874                 }
01875                 if (!word[1]) {
01876                         const int shiftflag = 0x1000000;
01877                         const int map[256] = {
01878                                 0,0,0,0,0,0,0,0,
01879                                 KBD_backspace,
01880                                 KBD_tab,
01881                                 KBD_enter,
01882                                 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
01883                                 KBD_esc,
01884                                 0,0,0,0,
01885                                 KBD_space, KBD_1|shiftflag, KBD_quote|shiftflag, KBD_3|shiftflag, KBD_4|shiftflag, KBD_5|shiftflag, KBD_7|shiftflag, KBD_quote,
01886                                 KBD_9|shiftflag, KBD_0|shiftflag, KBD_8|shiftflag, KBD_equals|shiftflag, KBD_comma, KBD_minus, KBD_period, KBD_slash,
01887                                 KBD_0, KBD_1, KBD_2, KBD_3, KBD_4, KBD_5, KBD_6, KBD_7,
01888                                 KBD_8, KBD_9, KBD_semicolon|shiftflag, KBD_semicolon, KBD_comma|shiftflag, KBD_equals, KBD_period|shiftflag, KBD_slash|shiftflag,
01889                                 KBD_2|shiftflag, KBD_a|shiftflag, KBD_b|shiftflag, KBD_c|shiftflag, KBD_d|shiftflag, KBD_e|shiftflag, KBD_f|shiftflag, KBD_g|shiftflag,
01890                                 KBD_h|shiftflag, KBD_i|shiftflag, KBD_j|shiftflag, KBD_k|shiftflag, KBD_l|shiftflag, KBD_m|shiftflag, KBD_n|shiftflag, KBD_o|shiftflag,
01891                                 KBD_p|shiftflag, KBD_q|shiftflag, KBD_r|shiftflag, KBD_s|shiftflag, KBD_t|shiftflag, KBD_u|shiftflag, KBD_v|shiftflag, KBD_w|shiftflag,
01892                                 KBD_x|shiftflag, KBD_y|shiftflag, KBD_z|shiftflag, KBD_leftbracket, KBD_backslash, KBD_rightbracket, KBD_6|shiftflag, KBD_minus|shiftflag,
01893                                 KBD_grave, KBD_a, KBD_b, KBD_c, KBD_d, KBD_e, KBD_f, KBD_g,
01894                                 KBD_h, KBD_i, KBD_j, KBD_k, KBD_l, KBD_m, KBD_n, KBD_o,
01895                                 KBD_p, KBD_q, KBD_r, KBD_s, KBD_t, KBD_u, KBD_v, KBD_w,
01896                                 KBD_x, KBD_y, KBD_z, KBD_leftbracket|shiftflag, KBD_backslash|shiftflag, KBD_rightbracket|shiftflag, KBD_grave|shiftflag, 0,
01897                                 0, KBD_f1, KBD_f2, KBD_f3, KBD_f4, KBD_f5, KBD_f6, KBD_f7, KBD_f8, KBD_f9, KBD_f10, KBD_f11, KBD_f12,
01898                                 KBD_up, KBD_down, KBD_left, KBD_right, KBD_insert, KBD_delete, KBD_home, KBD_end, KBD_pageup, KBD_pagedown,
01899                                 KBD_kp0, KBD_kp1, KBD_kp2, KBD_kp3, KBD_kp4, KBD_kp5, KBD_kp6, KBD_kp7, KBD_kp8, KBD_kp9, 
01900                         };
01901                         scankey = (KBD_KEYS)(map[(unsigned char)word[0]] & ~shiftflag);
01902                         if (map[(unsigned char)word[0]] & shiftflag) shift = true;
01903                         if (!scankey && core == 0) {
01904                                 WriteOut(MSG_Get("SHELL_SYNTAXERROR"),word);
01905                                 return;
01906                         }
01907                         if (core == 0) word[0] = 0;
01908                 }
01909                 if (word[0] == 'p') {
01910                         delay += strtol(word+1,&tail,0);
01911                         if (tail && *tail) {
01912                                 WriteOut(MSG_Get("SHELL_SYNTAXERROR"),word);
01913                                 return;
01914                         }
01915                 } else if (word[0] == 'l') {
01916                         duration = strtol(word+1,&tail,0);
01917                         if (tail && *tail) {
01918                                 WriteOut(MSG_Get("SHELL_SYNTAXERROR"),word);
01919                                 return;
01920                         }
01921                 } else if (!word[0] || ((scankey = (KBD_KEYS)strtol(word,NULL,0)) > KBD_NONE && scankey < KBD_LAST)) {
01922                         if (shift) {
01923                                 if (delay == 0) KEYBOARD_AddKey(KBD_leftshift,true);
01924                                 else PIC_AddEvent(&delayed_press,delay++,KBD_leftshift);
01925                         }
01926                         if (control) {
01927                                 if (delay == 0) KEYBOARD_AddKey(KBD_leftctrl,true);
01928                                 else PIC_AddEvent(&delayed_press,delay++,KBD_leftctrl);
01929                         }
01930                         if (alt) {
01931                                 if (delay == 0) KEYBOARD_AddKey(KBD_leftalt,true);
01932                         else PIC_AddEvent(&delayed_press,delay++,KBD_leftalt);
01933                         }
01934                         if (delay == 0) KEYBOARD_AddKey(scankey,true);
01935                         else PIC_AddEvent(&delayed_press,delay++,scankey);
01936 
01937                         if (delay+duration == 0) KEYBOARD_AddKey(scankey,false);
01938                         else PIC_AddEvent(&delayed_release,delay+++duration,scankey);
01939                         if (alt) {
01940                                 if (delay+duration == 0) KEYBOARD_AddKey(KBD_leftalt,false);
01941                                 else PIC_AddEvent(&delayed_release,delay+++duration,KBD_leftalt);
01942                         }
01943                         if (control) {
01944                                 if (delay+duration == 0) KEYBOARD_AddKey(KBD_leftctrl,false);
01945                                 else PIC_AddEvent(&delayed_release,delay+++duration,KBD_leftctrl);
01946                         }
01947                         if (shift) {
01948                                 if (delay+duration == 0) KEYBOARD_AddKey(KBD_leftshift,false);
01949                                 else PIC_AddEvent(&delayed_release,delay+++duration,KBD_leftshift);
01950                         }
01951                 } else if (core != 0) {
01952                         if (core == 1) {
01953                                 if (delay == 0) SetVal("cpu","core","normal");
01954                                 else PIC_AddEvent(&delayed_sdlpress,delay++,1);
01955                         } else if (core == 2) {
01956                                 if (delay == 0) SetVal("cpu","core","simple");
01957                                 else PIC_AddEvent(&delayed_sdlpress,delay++,2);
01958                         } else if (core == 3) {
01959                                 if (delay == 0) SetVal("cpu","core","dynamic");
01960                                 else PIC_AddEvent(&delayed_sdlpress,delay++,3);
01961                         } else if (core == 4) {
01962                                 if (delay == 0) SetVal("cpu","core","full");
01963                                 else PIC_AddEvent(&delayed_sdlpress,delay++,4);
01964                         }
01965                 } else {
01966                         WriteOut(MSG_Get("SHELL_SYNTAXERROR"),word);
01967                         return;
01968                 }
01969         }
01970  }
01971 
01972 #if C_DEBUG
01973 bool debugger_break_on_exec = false;
01974 
01975 void DOS_Shell::CMD_DEBUGBOX(char * args) {
01976     /* TODO: The command as originally taken from DOSBox SVN supported a /NOMOUSE option to remove the INT 33h vector */
01977     debugger_break_on_exec = true;
01978     while (*args == ' ') args++;
01979     DoCommand(args);
01980     debugger_break_on_exec = false;
01981 }
01982 #endif
01983 
01984 void DOS_Shell::CMD_FOR(char *args){
01985     (void)args;//UNUSED
01986 }
01987 
01988 void CAPTURE_StartCapture(void);
01989 void CAPTURE_StopCapture(void);
01990 
01991 void CAPTURE_StartWave(void);
01992 void CAPTURE_StopWave(void);
01993 
01994 void CAPTURE_StartMTWave(void);
01995 void CAPTURE_StopMTWave(void);
01996 
01997 // Explanation: Start capture, run program, stop capture when program exits.
01998 //              Great for gameplay footage or demoscene capture.
01999 //
02000 //              The command name is chosen not to conform to the 8.3 pattern
02001 //              on purpose to avoid conflicts with any existing DOS applications.
02002 void DOS_Shell::CMD_DXCAPTURE(char * args) {
02003     bool cap_video = false;
02004     bool cap_audio = false;
02005     bool cap_mtaudio = false;
02006     unsigned long post_exit_delay_ms = 3000; /* 3 sec */
02007 
02008     while (*args == ' ') args++;
02009 
02010     if (ScanCMDBool(args,"V"))
02011         cap_video = true;
02012     if (ScanCMDBool(args,"-V"))
02013         cap_video = false;
02014 
02015     if (ScanCMDBool(args,"A"))
02016         cap_audio = true;
02017     if (ScanCMDBool(args,"-A"))
02018         cap_audio = false;
02019 
02020     if (ScanCMDBool(args,"M"))
02021         cap_mtaudio = true;
02022     if (ScanCMDBool(args,"-M"))
02023         cap_mtaudio = false;
02024 
02025     if (!cap_video && !cap_audio && !cap_mtaudio)
02026         cap_video = true;
02027 
02028     if (cap_video)
02029         CAPTURE_StartCapture();
02030     if (cap_audio)
02031         CAPTURE_StartWave();
02032     if (cap_mtaudio)
02033         CAPTURE_StartMTWave();
02034 
02035     DoCommand(args);
02036 
02037     if (post_exit_delay_ms > 0) {
02038         LOG_MSG("Pausing for post exit delay (%.3f seconds)",(double)post_exit_delay_ms / 1000);
02039 
02040         Bit32u lasttick=GetTicks();
02041         while ((GetTicks()-lasttick)<post_exit_delay_ms) {
02042             CALLBACK_Idle();
02043 
02044             if (machine == MCH_PC98) {
02045                 reg_eax = 0x0100;   // sense key
02046                 CALLBACK_RunRealInt(0x18);
02047                 SETFLAGBIT(ZF,reg_bh == 0);
02048             }
02049             else {
02050                 reg_eax = 0x0100;
02051                 CALLBACK_RunRealInt(0x16);
02052             }
02053 
02054             if (!GETFLAG(ZF)) {
02055                 if (machine == MCH_PC98) {
02056                     reg_eax = 0x0000;   // read key
02057                     CALLBACK_RunRealInt(0x18);
02058                 }
02059                 else {
02060                     reg_eax = 0x0000;
02061                     CALLBACK_RunRealInt(0x16);
02062                 }
02063 
02064                 if (reg_al == 32/*space*/ || reg_al == 27/*escape*/)
02065                     break;
02066             }
02067         }
02068     }
02069 
02070     if (cap_video)
02071         CAPTURE_StopCapture();
02072     if (cap_audio)
02073         CAPTURE_StopWave();
02074     if (cap_mtaudio)
02075         CAPTURE_StopMTWave();
02076 }
02077 
02078 void DOS_Shell::CMD_CTTY(char * args) {
02079         /* NTS: This is written to emulate the simplistic parsing in MS-DOS 6.22 */
02080         Bit16u handle;
02081         int i;
02082 
02083         /* args has leading space? */
02084         args = trim(args);
02085 
02086         /* must be device */
02087         if (DOS_FindDevice(args) == DOS_DEVICES) {
02088                 WriteOut("Invalid device");
02089                 return;
02090         }
02091 
02092         /* close STDIN/STDOUT/STDERR and replace with new handle */
02093         if (!DOS_OpenFile(args,OPEN_READWRITE,&handle)) {
02094                 WriteOut("Unable to open device");
02095                 return;
02096         }
02097 
02098         for (i=0;i < 3;i++) {
02099                 DOS_CloseFile(i);
02100                 DOS_ForceDuplicateEntry(handle,i);
02101         }
02102         DOS_CloseFile(handle);
02103 }
02104