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