DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/shell/shell_cmds.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  *  Heavy improvements by the DOSBox-X Team, 2011-2020
00019  *  DXCAPTURE, DEBUGBOX, INT2FDBG commands by joncampbell123
00020  *  ATTRIB, COUNTRY, FOR, LFNFOR, VERIFY, TRUENAME commands by Wengier
00021  *  LS command by the dosbox-staging team and Wengier
00022  */
00023 
00024 
00025 #include "dosbox.h"
00026 #include "shell.h"
00027 #include "callback.h"
00028 #include "regs.h"
00029 #include "pic.h"
00030 #include "keyboard.h"
00031 #include "timer.h"
00032 #include "../src/ints/int10.h"
00033 #include <time.h>
00034 #include <assert.h>
00035 #include "bios.h"
00036 #include "../dos/drives.h"
00037 #include "support.h"
00038 #include "control.h"
00039 #include "paging.h"
00040 #include <algorithm>
00041 #include <cstring>
00042 #include <cctype>
00043 #include <cstdlib>
00044 #include <vector>
00045 #include <string>
00046 #include "build_timestamp.h"
00047 
00048 #if defined(_MSC_VER)
00049 # pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */
00050 #endif
00051 
00052 static SHELL_Cmd cmd_list[]={
00053 {       "DIR",                  0,              &DOS_Shell::CMD_DIR,            "SHELL_CMD_DIR_HELP"},
00054 {       "CD",                   0,              &DOS_Shell::CMD_CHDIR,          "SHELL_CMD_CHDIR_HELP"},
00055 {       "ALIAS",                1,              &DOS_Shell::CMD_ALIAS,          "SHELL_CMD_ALIAS_HELP"},
00056 {       "ATTRIB",               1,              &DOS_Shell::CMD_ATTRIB,         "SHELL_CMD_ATTRIB_HELP"},
00057 {       "BREAK",                1,              &DOS_Shell::CMD_BREAK,          "SHELL_CMD_BREAK_HELP"},
00058 {       "CALL",                 1,              &DOS_Shell::CMD_CALL,           "SHELL_CMD_CALL_HELP"},
00059 {       "CHDIR",                1,              &DOS_Shell::CMD_CHDIR,          "SHELL_CMD_CHDIR_HELP"},
00060 {       "CHOICE",               1,              &DOS_Shell::CMD_CHOICE,         "SHELL_CMD_CHOICE_HELP"},
00061 {       "CLS",                  0,              &DOS_Shell::CMD_CLS,            "SHELL_CMD_CLS_HELP"},
00062 {       "COPY",                 0,              &DOS_Shell::CMD_COPY,           "SHELL_CMD_COPY_HELP"},
00063 {       "COUNTRY",              1,              &DOS_Shell::CMD_COUNTRY,        "SHELL_CMD_COUNTRY_HELP"},
00064 {       "CTTY",                 1,              &DOS_Shell::CMD_CTTY,           "SHELL_CMD_CTTY_HELP"},
00065 {       "DATE",                 0,              &DOS_Shell::CMD_DATE,           "SHELL_CMD_DATE_HELP"},
00066 {       "DEL",                  0,              &DOS_Shell::CMD_DELETE,         "SHELL_CMD_DELETE_HELP"},
00067 {       "ECHO",                 0,              &DOS_Shell::CMD_ECHO,           "SHELL_CMD_ECHO_HELP"},
00068 {       "ERASE",                1,              &DOS_Shell::CMD_DELETE,         "SHELL_CMD_DELETE_HELP"},
00069 {       "EXIT",                 0,              &DOS_Shell::CMD_EXIT,           "SHELL_CMD_EXIT_HELP"}, 
00070 {       "FOR",                  1,              &DOS_Shell::CMD_FOR,            "SHELL_CMD_FOR_HELP"},
00071 {       "GOTO",                 1,              &DOS_Shell::CMD_GOTO,           "SHELL_CMD_GOTO_HELP"},
00072 {       "HELP",                 1,              &DOS_Shell::CMD_HELP,           "SHELL_CMD_HELP_HELP"},
00073 {       "IF",                   1,              &DOS_Shell::CMD_IF,                     "SHELL_CMD_IF_HELP"},
00074 {       "LFNFOR",               1,              &DOS_Shell::CMD_LFNFOR,         "SHELL_CMD_LFNFOR_HELP"},
00075 {       "LH",                   1,              &DOS_Shell::CMD_LOADHIGH,       "SHELL_CMD_LOADHIGH_HELP"},
00076 {       "LOADHIGH",             1,              &DOS_Shell::CMD_LOADHIGH,       "SHELL_CMD_LOADHIGH_HELP"},
00077 {   "LS",                       1,              &DOS_Shell::CMD_LS,                     "SHELL_CMD_LS_HELP"},
00078 {       "MD",                   0,              &DOS_Shell::CMD_MKDIR,          "SHELL_CMD_MKDIR_HELP"},
00079 {       "MKDIR",                1,              &DOS_Shell::CMD_MKDIR,          "SHELL_CMD_MKDIR_HELP"},
00080 {       "MORE",                 1,              &DOS_Shell::CMD_MORE,           "SHELL_CMD_MORE_HELP"},
00081 {       "PATH",                 1,              &DOS_Shell::CMD_PATH,           "SHELL_CMD_PATH_HELP"},
00082 {       "PAUSE",                1,              &DOS_Shell::CMD_PAUSE,          "SHELL_CMD_PAUSE_HELP"},
00083 {       "PROMPT",               0,              &DOS_Shell::CMD_PROMPT,         "SHELL_CMD_PROMPT_HELP"},
00084 {       "RD",                   0,              &DOS_Shell::CMD_RMDIR,          "SHELL_CMD_RMDIR_HELP"},
00085 {       "REM",                  1,              &DOS_Shell::CMD_REM,            "SHELL_CMD_REM_HELP"},
00086 {       "REN",                  0,              &DOS_Shell::CMD_RENAME,         "SHELL_CMD_RENAME_HELP"},
00087 {       "RENAME",               1,              &DOS_Shell::CMD_RENAME,         "SHELL_CMD_RENAME_HELP"},
00088 {       "RMDIR",                1,              &DOS_Shell::CMD_RMDIR,          "SHELL_CMD_RMDIR_HELP"},
00089 {       "SET",                  1,              &DOS_Shell::CMD_SET,            "SHELL_CMD_SET_HELP"},
00090 {       "SHIFT",                1,              &DOS_Shell::CMD_SHIFT,          "SHELL_CMD_SHIFT_HELP"},
00091 {       "SUBST",                1,              &DOS_Shell::CMD_SUBST,          "SHELL_CMD_SUBST_HELP"},
00092 {       "TIME",                 0,              &DOS_Shell::CMD_TIME,           "SHELL_CMD_TIME_HELP"},
00093 {       "TYPE",                 0,              &DOS_Shell::CMD_TYPE,           "SHELL_CMD_TYPE_HELP"},
00094 {       "VER",                  0,              &DOS_Shell::CMD_VER,            "SHELL_CMD_VER_HELP"},
00095 {       "VERIFY",               1,              &DOS_Shell::CMD_VERIFY,         "SHELL_CMD_VERIFY_HELP"},
00096 {       "VOL",                  0,              &DOS_Shell::CMD_VOL,            "SHELL_CMD_VOL_HELP"},
00097 {       "TRUENAME",             1,              &DOS_Shell::CMD_TRUENAME,       "SHELL_CMD_TRUENAME_HELP"},
00098 // Advanced commands specific to DOSBox-X
00099 {       "ADDKEY",               1,              &DOS_Shell::CMD_ADDKEY,         "SHELL_CMD_ADDKEY_HELP"},
00100 {       "DX-CAPTURE",   1,              &DOS_Shell::CMD_DXCAPTURE,  "SHELL_CMD_DXCAPTURE_HELP"},
00101 #if C_DEBUG
00102 // Additional commands for debugging purposes in DOSBox-X
00103 {       "DEBUGBOX",             1,              &DOS_Shell::CMD_DEBUGBOX,       "SHELL_CMD_DEBUGBOX_HELP"},
00104 {       "INT2FDBG",             1,              &DOS_Shell::CMD_INT2FDBG,       "SHELL_CMD_INT2FDBG_HELP"},
00105 #endif
00106 {0,0,0,0}
00107 }; 
00108 
00109 extern int enablelfn, lfn_filefind_handle;
00110 extern bool date_host_forced, usecon, rsize;
00111 extern unsigned long freec;
00112 extern Bit16u countryNo;
00113 void DOS_SetCountry(Bit16u countryNo);
00114 
00115 /* support functions */
00116 static char empty_char = 0;
00117 static char* empty_string = &empty_char;
00118 static void StripSpaces(char*&args) {
00119         while(args && *args && isspace(*reinterpret_cast<unsigned char*>(args)))
00120                 args++;
00121 }
00122 
00123 static void StripSpaces(char*&args,char also) {
00124         while(args && *args && (isspace(*reinterpret_cast<unsigned char*>(args)) || (*args == also)))
00125                 args++;
00126 }
00127 
00128 static char* ExpandDot(char*args, char* buffer , size_t bufsize) {
00129         if(*args == '.') {
00130                 if(*(args+1) == 0){
00131                         safe_strncpy(buffer, "*.*", bufsize);
00132                         return buffer;
00133                 }
00134                 if( (*(args+1) != '.') && (*(args+1) != '\\') ) {
00135                         buffer[0] = '*';
00136                         buffer[1] = 0;
00137                         if (bufsize > 2) strncat(buffer,args,bufsize - 1 /*used buffer portion*/ - 1 /*trailing zero*/  );
00138                         return buffer;
00139                 } else
00140                         safe_strncpy (buffer, args, bufsize);
00141         }
00142         else safe_strncpy(buffer,args, bufsize);
00143         return buffer;
00144 }
00145 
00146 
00147 
00148 bool DOS_Shell::CheckConfig(char* cmd_in,char*line) {
00149         Section* test = control->GetSectionFromProperty(cmd_in);
00150         if(!test) return false;
00151         if(line && !line[0]) {
00152                 std::string val = test->GetPropValue(cmd_in);
00153                 if(val != NO_SUCH_PROPERTY) WriteOut("%s\n",val.c_str());
00154                 return true;
00155         }
00156         char newcom[1024]; newcom[0] = 0; strcpy(newcom,"z:\\config -set ");
00157         strcat(newcom,test->GetName()); strcat(newcom," ");
00158         strcat(newcom,cmd_in);
00159         if (line != NULL)
00160                 strcat(newcom, line);
00161         else
00162                 E_Exit("'line' in CheckConfig is NULL");
00163         DoCommand(newcom);
00164         return true;
00165 }
00166 
00167 bool enable_config_as_shell_commands = false;
00168 
00169 void DOS_Shell::DoCommand(char * line) {
00170 /* First split the line into command and arguments */
00171     char* orign_cmd_line = line;
00172     std::string last_alias_cmd;
00173     std::string altered_cmd_line;
00174     int alias_counter = 0;
00175 __do_command_begin:
00176     if (alias_counter > 64) {
00177         WriteOut(MSG_Get("SHELL_EXECUTE_ALIAS_EXPAND_OVERFLOW"), orign_cmd_line);
00178     }
00179         line=trim(line);
00180         char cmd_buffer[CMD_MAXLINE];
00181         char * cmd_write=cmd_buffer;
00182         int q=0;
00183         while (*line) {
00184         if (strchr("/\t", *line) || (q / 2 * 2 == q && strchr(" =", *line)))
00185                         break;
00186                 if (*line == '"') q++;
00187 //              if (*line == ':') break; //This breaks drive switching as that is handled at a later stage. 
00188                 if ((*line == '.') ||(*line == '\\')) {  //allow stuff like cd.. and dir.exe cd\kees
00189                         *cmd_write=0;
00190                         Bit32u cmd_index=0;
00191                         while (cmd_list[cmd_index].name) {
00192                                 if (strcasecmp(cmd_list[cmd_index].name,cmd_buffer)==0) {
00193                                         (this->*(cmd_list[cmd_index].handler))(line);
00194                                         return;
00195                                 }
00196                                 cmd_index++;
00197                         }
00198                 }
00199                 *cmd_write++=*line++;
00200         }
00201         *cmd_write=0;
00202         if (strlen(cmd_buffer)==0) {
00203                 if (strlen(line)&&line[0]=='/') WriteOut(MSG_Get("SHELL_EXECUTE_ILLEGAL_COMMAND"),line);
00204                 return;
00205         }
00206     cmd_alias_map_t::iterator iter = cmd_alias.find(cmd_buffer);
00207     if (iter != cmd_alias.end() && last_alias_cmd != cmd_buffer) {
00208         alias_counter++;
00209         altered_cmd_line = iter->second + " " + line;
00210         line = (char*)altered_cmd_line.c_str();
00211         last_alias_cmd = iter->first;
00212         goto __do_command_begin;
00213     }
00214 
00215 /* Check the internal list */
00216         Bit32u cmd_index=0;
00217         while (cmd_list[cmd_index].name) {
00218                 if (strcasecmp(cmd_list[cmd_index].name,cmd_buffer)==0) {
00219                         (this->*(cmd_list[cmd_index].handler))(line);
00220                         return;
00221                 }
00222                 cmd_index++;
00223         }
00224 /* This isn't an internal command execute it */
00225         char ldir[CROSS_LEN], *p=ldir;
00226         if (strchr(cmd_buffer,'\"')&&DOS_GetSFNPath(cmd_buffer,ldir,false)) {
00227                 if (!strchr(cmd_buffer, '\\') && strrchr(ldir, '\\'))
00228                         p=strrchr(ldir, '\\')+1;
00229                 if (uselfn&&strchr(p, ' ')&&!DOS_FileExists(("\""+std::string(p)+"\"").c_str())) {
00230                         bool append=false;
00231                         if (DOS_FileExists(("\""+std::string(p)+".COM\"").c_str())) {append=true;strcat(p, ".COM");}
00232                         else if (DOS_FileExists(("\""+std::string(p)+".EXE\"").c_str())) {append=true;strcat(p, ".EXE");}
00233                         else if (DOS_FileExists(("\""+std::string(p)+".BAT\"").c_str())) {append=true;strcat(p, ".BAT");}
00234                         if (append&&DOS_GetSFNPath(("\""+std::string(p)+"\"").c_str(), cmd_buffer,false)) if(Execute(cmd_buffer,line)) return;
00235                 }
00236                 if(Execute(p,line)) return;
00237         } else
00238                 if(Execute(cmd_buffer,line)) return;
00239         if(enable_config_as_shell_commands && CheckConfig(cmd_buffer,line)) return;
00240         WriteOut(MSG_Get("SHELL_EXECUTE_ILLEGAL_COMMAND"),cmd_buffer);
00241 }
00242 
00243 #define HELP(command) \
00244         if (ScanCMDBool(args,"?")) { \
00245                 WriteOut(MSG_Get("SHELL_CMD_" command "_HELP")); \
00246                 const char* long_m = MSG_Get("SHELL_CMD_" command "_HELP_LONG"); \
00247                 WriteOut("\n"); \
00248                 if(strcmp("Message not Found!\n",long_m)) WriteOut(long_m); \
00249                 else WriteOut(command "\n"); \
00250                 return; \
00251         }
00252 
00253 #if C_DEBUG
00254 Bitu int2fdbg_hook_callback = 0;
00255 
00256 static Bitu INT2FDBG_Handler(void) {
00257         if (reg_ax == 0x1605) { /* Windows init broadcast */
00258                 int patience = 500;
00259                 Bitu st_seg,st_ofs;
00260 
00261                 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",
00262                         SegValue(es),reg_bx,
00263                         SegValue(ds),reg_si,
00264                         reg_cx,reg_dx,reg_di);
00265 
00266                 st_seg = SegValue(es);
00267                 st_ofs = reg_bx;
00268                 while (st_seg != 0 || st_ofs != 0) {
00269                         unsigned char v_major,v_minor;
00270                         Bitu st_seg_next,st_ofs_next;
00271                         Bitu idrc_seg,idrc_ofs;
00272                         Bitu vdev_seg,vdev_ofs;
00273                         Bitu name_seg,name_ofs;
00274                         char devname[64];
00275                         PhysPt st_o;
00276 
00277                         if (--patience <= 0) {
00278                                 LOG_MSG("**WARNING: Chain is too long. Something might have gotten corrupted\n");
00279                                 break;
00280                         }
00281 
00282                         st_o = PhysMake(st_seg,st_ofs);
00283                         /* +0x00: Major, minor version of info structure
00284                          * +0x02: pointer to next startup info structure or 0000:0000
00285                          * +0x06: pointer to ASCIIZ name of virtual device or 0000:0000
00286                          * +0x0A: virtual device ref data (pointer to?? or actual data??) or 0000:0000
00287                          * +0x0E: pointer to instance data records or 0000:0000
00288                          * Windows 95 or later (v4.0+):
00289                          * +0x12: pointer to optionally-instanced data records or 0000:0000 */
00290                         v_major = mem_readb(st_o+0x00);
00291                         v_minor = mem_readb(st_o+0x01);
00292                         st_seg_next = mem_readw(st_o+0x02+2);
00293                         st_ofs_next = mem_readw(st_o+0x02+0);
00294                         name_ofs = mem_readw(st_o+0x06+0);
00295                         name_seg = mem_readw(st_o+0x06+2);
00296                         vdev_ofs = mem_readw(st_o+0x0A+0);
00297                         vdev_seg = mem_readw(st_o+0x0A+2);
00298                         idrc_ofs = mem_readw(st_o+0x0A+4);      /* FIXME: 0x0E+0 and 0x0E+2 generates weird compiler error WTF?? */
00299                         idrc_seg = mem_readw(st_o+0x0A+6);
00300 
00301                         {
00302                                 devname[0] = 0;
00303                                 if (name_seg != 0 || name_ofs != 0) {
00304                                         unsigned char c;
00305                                         unsigned int i;
00306                                         PhysPt scan;
00307 
00308                                         scan = PhysMake(name_seg,name_ofs);
00309                                         for (i=0;i < 63 && (c=mem_readb(scan++)) != 0;) devname[i++] = (char)c;
00310                                         devname[i] = 0;
00311                                 }
00312                         }
00313 
00314                         LOG_MSG(" >> Version %u.%u\n",v_major,v_minor);
00315                         LOG_MSG("    Next entry at %04x:%04x\n",(int)st_seg_next,(int)st_ofs_next);
00316                         LOG_MSG("    Virtual device name: %04x:%04x '%s'\n",(int)name_seg,(int)name_ofs,devname);
00317                         LOG_MSG("    Virtual dev ref data: %04x:%04x\n",(int)vdev_seg,(int)vdev_ofs);
00318                         LOG_MSG("    Instance data records: %04x:%04x\n",(int)idrc_seg,(int)idrc_ofs);
00319 
00320                         st_seg = st_seg_next;
00321                         st_ofs = st_ofs_next;
00322                 }
00323 
00324                 LOG_MSG("----END CHAIN\n");
00325         }
00326 
00327         return CBRET_NONE;
00328 }
00329 
00330 /* NTS: I know I could just modify the DOS kernel's INT 2Fh code to receive the init call,
00331  *      the problem is that at that point, the registers do not yet contain anything interesting.
00332  *      all the interesting results of the call are added by TSRs on the way back UP the call
00333  *      chain. The purpose of this program therefore is to hook INT 2Fh on the other end
00334  *      of the call chain so that we can see the results just before returning INT 2Fh back
00335  *      to WIN.COM */
00336 void DOS_Shell::CMD_INT2FDBG(char * args) {
00337         HELP("INT2FDBG");
00338     while (*args == ' ') args++;
00339     if (!strcmp(args,"-?")) {
00340                 args[0]='/';
00341                 HELP("INT2FDBG");
00342                 return;
00343         }
00344 
00345         /* TODO: Allow /U to remove INT 2Fh hook */
00346         if (ScanCMDBool(args,"I")) {
00347                 if (int2fdbg_hook_callback == 0) {
00348                         Bit32u old_int2Fh;
00349                         PhysPt w;
00350 
00351                         int2fdbg_hook_callback = CALLBACK_Allocate();
00352                         CALLBACK_Setup(int2fdbg_hook_callback,&INT2FDBG_Handler,CB_IRET,"INT 2Fh DBG callback");
00353 
00354                         /* record old vector, set our new vector */
00355                         old_int2Fh = RealGetVec(0x2f);
00356                         w = CALLBACK_PhysPointer(int2fdbg_hook_callback);
00357                         RealSetVec(0x2f,CALLBACK_RealPointer(int2fdbg_hook_callback));
00358 
00359                         /* overwrite the callback with code to chain the call down, then invoke our callback on the way back up: */
00360 
00361                         /* first, chain to the previous INT 15h handler */
00362                         phys_writeb(w++,(Bit8u)0x9C);                                   //PUSHF
00363                         phys_writeb(w++,(Bit8u)0x9A);                                   //CALL FAR <address>
00364                         phys_writew(w,(Bit16u)(old_int2Fh&0xFFFF)); w += 2;             //offset
00365                         phys_writew(w,(Bit16u)((old_int2Fh>>16)&0xFFFF)); w += 2;       //seg
00366 
00367                         /* then, having returned from it, invoke our callback */
00368                         phys_writeb(w++,(Bit8u)0xFE);                                   //GRP 4
00369                         phys_writeb(w++,(Bit8u)0x38);                                   //Extra Callback instruction
00370                         phys_writew(w,(Bit16u)int2fdbg_hook_callback); w += 2;          //The immediate word
00371 
00372                         /* return */
00373                         phys_writeb(w++,(Bit8u)0xCF);                                   //IRET
00374 
00375                         LOG_MSG("INT 2Fh debugging hook set\n");
00376                         WriteOut("INT 2Fh hook set\n");
00377                 }
00378                 else {
00379                         WriteOut("INT 2Fh hook already setup\n");
00380                 }
00381         }
00382         else if (*args)
00383                 WriteOut("Invalid parameter - %s\n", args);
00384         else
00385                 WriteOut("%s\n%s", MSG_Get("SHELL_CMD_INT2FDBG_HELP"), MSG_Get("SHELL_CMD_INT2FDBG_HELP_LONG"));
00386 }
00387 #endif
00388 
00389 void DOS_Shell::CMD_BREAK(char * args) {
00390         HELP("BREAK");
00391         args = trim(args);
00392         if (!*args)
00393                 WriteOut("BREAK is %s\n", dos.breakcheck ? "on" : "off");
00394         else if (!strcasecmp(args, "OFF"))
00395                 dos.breakcheck = false;
00396         else if (!strcasecmp(args, "ON"))
00397                 dos.breakcheck = true;
00398         else
00399                 WriteOut("Must specify ON or OFF\n");
00400 }
00401 
00402 void DOS_Shell::CMD_CLS(char * args) {
00403         HELP("CLS");
00404    if (CurMode->type==M_TEXT || IS_PC98_ARCH)
00405        WriteOut("");
00406    else { 
00407       reg_ax=(Bit16u)CurMode->mode; 
00408       CALLBACK_RunRealInt(0x10); 
00409    } 
00410 }
00411 
00412 void DOS_Shell::CMD_DELETE(char * args) {
00413         HELP("DELETE");
00414         bool optP=ScanCMDBool(args,"P");
00415         bool optF=ScanCMDBool(args,"F");
00416         bool optQ=ScanCMDBool(args,"Q");
00417 
00418         // ignore /f, /s, /ar, /as, /ah and /aa switches for compatibility
00419         ScanCMDBool(args,"S");
00420         ScanCMDBool(args,"AR");
00421         ScanCMDBool(args,"AS");
00422         ScanCMDBool(args,"AH");
00423         ScanCMDBool(args,"AA");
00424 
00425         char * rem=ScanCMDRemain(args);
00426         if (rem) {
00427                 WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
00428                 return;
00429         }
00430         if (!*args) {
00431                 WriteOut(MSG_Get("SHELL_MISSING_PARAMETER"));
00432                 return;
00433         }
00434 
00435         StripSpaces(args);
00436         args=trim(args);
00437 
00438         /* Command uses dta so set it to our internal dta */
00439         //DOS_DTA dta(dos.dta());
00440         RealPt save_dta=dos.dta();
00441         dos.dta(dos.tables.tempdta);
00442         DOS_DTA dta(dos.dta());
00443         /* If delete accept switches mind the space in front of them. See the dir /p code */ 
00444 
00445         char full[DOS_PATHLENGTH],sfull[DOS_PATHLENGTH+2];
00446         char buffer[CROSS_LEN];
00447     char name[DOS_NAMELENGTH_ASCII],lname[LFN_NAMELENGTH+1];
00448     Bit32u size;Bit16u time,date;Bit8u attr;
00449         args = ExpandDot(args,buffer, CROSS_LEN);
00450         StripSpaces(args);
00451         if (!DOS_Canonicalize(args,full)) { WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));dos.dta(save_dta);return; }
00452         if (strlen(args)&&args[strlen(args)-1]!='\\') {
00453                 Bit16u fattr;
00454                 if (strcmp(args,"*.*")&&DOS_GetFileAttr(args, &fattr) && (fattr&DOS_ATTR_DIRECTORY))
00455                         strcat(args, "\\");
00456         }
00457         if (strlen(args)&&args[strlen(args)-1]=='\\') strcat(args, "*.*");
00458         else if (!strcmp(args,".")||(strlen(args)>1&&(args[strlen(args)-2]==':'||args[strlen(args)-2]=='\\')&&args[strlen(args)-1]=='.')) {
00459                 args[strlen(args)-1]='*';
00460                 strcat(args, ".*");
00461         } else if (uselfn&&strchr(args, '*')) {
00462                 char * find_last;
00463                 find_last=strrchr(args,'\\');
00464                 if (find_last==NULL) find_last=args;
00465                 else find_last++;
00466                 if (strlen(find_last)>0&&args[strlen(args)-1]=='*'&&strchr(find_last, '.')==NULL) strcat(args, ".*");
00467         }
00468         if (!strcmp(args,"*.*")||(strlen(args)>3&&(!strcmp(args+strlen(args)-4, "\\*.*") || !strcmp(args+strlen(args)-4, ":*.*")))) {
00469                 if (!optQ) {
00470 first_1:
00471                         WriteOut(MSG_Get("SHELL_CMD_DEL_SURE"));
00472 first_2:
00473                         Bit8u c;Bit16u n=1;
00474                         DOS_ReadFile (STDIN,&c,&n);
00475                         do switch (c) {
00476                         case 'n':                       case 'N':
00477                         {
00478                                 DOS_WriteFile (STDOUT,&c, &n);
00479                                 DOS_ReadFile (STDIN,&c,&n);
00480                                 do switch (c) {
00481                                         case 0xD: WriteOut("\n");dos.dta(save_dta);return;
00482                                         case 0x03: WriteOut("^C\n");dos.dta(save_dta);return;
00483                                         case 0x08: WriteOut("\b \b"); goto first_2;
00484                                 } while (DOS_ReadFile (STDIN,&c,&n));
00485                         }
00486                         case 'y':                       case 'Y':
00487                         {
00488                                 DOS_WriteFile (STDOUT,&c, &n);
00489                                 DOS_ReadFile (STDIN,&c,&n);
00490                                 do switch (c) {
00491                                         case 0xD: WriteOut("\n"); goto continue_1;
00492                                         case 0x03: WriteOut("^C\n");dos.dta(save_dta);return;
00493                                         case 0x08: WriteOut("\b \b"); goto first_2;
00494                                 } while (DOS_ReadFile (STDIN,&c,&n));
00495                         }
00496                         case 0xD: WriteOut("\n"); goto first_1;
00497                         case 0x03: WriteOut("^C\n");dos.dta(save_dta);return;
00498                         case '\t':
00499                         case 0x08:
00500                                 goto first_2;
00501                         default:
00502                         {
00503                                 DOS_WriteFile (STDOUT,&c, &n);
00504                                 DOS_ReadFile (STDIN,&c,&n);
00505                                 do switch (c) {
00506                                         case 0xD: WriteOut("\n"); goto first_1;
00507                                         case 0x03: WriteOut("^C\n");dos.dta(save_dta);return;
00508                                         case 0x08: WriteOut("\b \b"); goto first_2;
00509                                 } while (DOS_ReadFile (STDIN,&c,&n));
00510                                 goto first_2;
00511                         }
00512                 } while (DOS_ReadFile (STDIN,&c,&n));
00513         }
00514 }
00515 
00516 continue_1:
00517         /* Command uses dta so set it to our internal dta */
00518         if (!DOS_Canonicalize(args,full)) { WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));dos.dta(save_dta);return; }
00519         char path[DOS_PATHLENGTH], spath[DOS_PATHLENGTH], pattern[DOS_PATHLENGTH], *r=strrchr(full, '\\');
00520         if (r!=NULL) {
00521                 *r=0;
00522                 strcpy(path, full);
00523                 strcat(path, "\\");
00524                 strcpy(pattern, r+1);
00525                 *r='\\';
00526         } else {
00527                 strcpy(path, "");
00528                 strcpy(pattern, full);
00529         }
00530         int k=0;
00531         for (int i=0;i<(int)strlen(pattern);i++)
00532                 if (pattern[i]!='\"')
00533                         pattern[k++]=pattern[i];
00534         pattern[k]=0;
00535         strcpy(spath, path);
00536         if (strchr(args,'\"')||uselfn) {
00537                 if (!DOS_GetSFNPath(("\""+std::string(path)+"\\").c_str(), spath, false)) strcpy(spath, path);
00538                 if (!strlen(spath)||spath[strlen(spath)-1]!='\\') strcat(spath, "\\");
00539         }
00540         std::string pfull=std::string(spath)+std::string(pattern);
00541         int fbak=lfn_filefind_handle;
00542         lfn_filefind_handle=uselfn?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE;
00543     bool res=DOS_FindFirst((char *)((uselfn&&pfull.length()&&pfull[0]!='"'?"\"":"")+pfull+(uselfn&&pfull.length()&&pfull[pfull.length()-1]!='"'?"\"":"")).c_str(),0xffff & ~DOS_ATTR_VOLUME);
00544         if (!res) {
00545                 lfn_filefind_handle=fbak;
00546                 WriteOut(MSG_Get("SHELL_CMD_DEL_ERROR"),args);
00547                 dos.dta(save_dta);
00548                 return;
00549         }
00550         lfn_filefind_handle=fbak;
00551         //end can't be 0, but if it is we'll get a nice crash, who cares :)
00552         strcpy(sfull,full);
00553         char * end=strrchr(full,'\\')+1;*end=0;
00554         char * lend=strrchr(sfull,'\\')+1;*lend=0;
00555         dta=dos.dta();
00556         bool exist=false;
00557         lfn_filefind_handle=uselfn?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE;
00558         while (res) {
00559                 dta.GetResult(name,lname,size,date,time,attr);
00560                 if (!optF && (attr & DOS_ATTR_READ_ONLY) && !(attr & DOS_ATTR_DIRECTORY)) {
00561                         exist=true;
00562                         strcpy(end,name);
00563                         strcpy(lend,lname);
00564                         WriteOut(MSG_Get("SHELL_CMD_DEL_ERROR"),uselfn?sfull:full);
00565                 } else if (!(attr & DOS_ATTR_DIRECTORY)) {
00566                         exist=true;
00567                         strcpy(end,name);
00568                         strcpy(lend,lname);
00569                         if (optP) {
00570                                 WriteOut("Delete %s (Y/N)?", uselfn?sfull:full);
00571                                 Bit8u c;
00572                                 Bit16u n=1;
00573                                 DOS_ReadFile (STDIN,&c,&n);
00574                                 if (c==3) {WriteOut("^C\r\n");break;}
00575                                 c = c=='y'||c=='Y' ? 'Y':'N';
00576                                 WriteOut("%c\r\n", c);
00577                                 if (c=='N') {lfn_filefind_handle=uselfn?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE;res = DOS_FindNext();continue;}
00578                         }
00579                         if (strlen(full)) {
00580                                 std::string pfull=(uselfn||strchr(full, ' ')?(full[0]!='"'?"\"":""):"")+std::string(full)+(uselfn||strchr(full, ' ')?(full[strlen(full)-1]!='"'?"\"":""):"");
00581                                 bool reset=false;
00582                                 if (optF && (attr & DOS_ATTR_READ_ONLY)&&DOS_SetFileAttr(pfull.c_str(), attr & ~DOS_ATTR_READ_ONLY)) reset=true;
00583                                 if (!DOS_UnlinkFile(pfull.c_str())) {
00584                                         if (optF&&reset) DOS_SetFileAttr(pfull.c_str(), attr);
00585                                         WriteOut(MSG_Get("SHELL_CMD_DEL_ERROR"),uselfn?sfull:full);
00586                                 }
00587                         } else WriteOut(MSG_Get("SHELL_CMD_DEL_ERROR"),uselfn?sfull:full);
00588                 }
00589                 res=DOS_FindNext();
00590         }
00591         lfn_filefind_handle=fbak;
00592         if (!exist) WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args);
00593         dos.dta(save_dta);
00594 }
00595 
00596 static size_t GetPauseCount() {
00597         Bit16u rows;
00598         if (IS_PC98_ARCH)
00599                 rows=real_readb(0x60,0x113) & 0x01 ? 25 : 20;
00600         else
00601                 rows=real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
00602         return (rows > 3u) ? (rows - 3u) : 22u;
00603 }
00604 
00605 void DOS_Shell::CMD_HELP(char * args){
00606         HELP("HELP");
00607         bool optall=ScanCMDBool(args,"A")|ScanCMDBool(args,"ALL");
00608         /* Print the help */
00609         args = trim(args);
00610         upcase(args);
00611         if(!optall&&!*args) WriteOut(MSG_Get("SHELL_CMD_HELP"));
00612         Bit32u cmd_index=0,write_count=0;
00613         bool show=false;
00614         while (cmd_list[cmd_index].name) {
00615                 if (optall || (*args && !strcmp(args, cmd_list[cmd_index].name)) || (!*args && !cmd_list[cmd_index].flags)) {
00616                         show=true;
00617                         if (*args && !strcmp(args, cmd_list[cmd_index].name) && !optall) {
00618                                 std::string cmd=std::string(args);
00619                                 if (cmd=="CD") cmd="CHDIR";
00620                                 else if (cmd=="DEL"||cmd=="ERASE") cmd="DELETE";
00621                                 else if (cmd=="LH") cmd="LOADHIGH";
00622                                 else if (cmd=="MD") cmd="MKDIR";
00623                                 else if (cmd=="RD") cmd="RMDIR";
00624                                 else if (cmd=="REN") cmd="RENAME";
00625                                 else if (cmd=="DX-CAPTURE") cmd="DXCAPTURE";
00626                                 WriteOut("%s\n%s",MSG_Get(cmd_list[cmd_index].help), MSG_Get(("SHELL_CMD_" +cmd+ "_HELP_LONG").c_str()));
00627                         } else {
00628                                 WriteOut("<\033[34;1m%-8s\033[0m> %s",cmd_list[cmd_index].name,MSG_Get(cmd_list[cmd_index].help));
00629                                 if(!(++write_count%GetPauseCount())) {
00630                                         WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
00631                                         Bit8u c;Bit16u n=1;
00632                                         DOS_ReadFile(STDIN,&c,&n);
00633                                         if (c==3) {WriteOut("^C\r\n");break;}
00634                                         if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
00635                                 }
00636                         }
00637                 }
00638                 cmd_index++;
00639         }
00640         if (*args&&!show) {
00641                 std::string argc=std::string(StripArg(args));
00642                 if (argc!=""&&argc!="CWSDPMI") DoCommand((char *)(argc+(argc=="DOS4GW"||argc=="DOS32A"?"":" /?")).c_str());
00643         }
00644 }
00645 
00646 static void removeChar(char *str, char c) {
00647     char *src, *dst;
00648     for (src = dst = str; *src != '\0'; src++) {
00649         *dst = *src;
00650         if (*dst != c) dst++;
00651     }
00652     *dst = '\0';
00653 }
00654 
00655 void DOS_Shell::CMD_RENAME(char * args){
00656         HELP("RENAME");
00657         StripSpaces(args);
00658         char * rem=ScanCMDRemain(args);
00659         if (rem) {
00660                 WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
00661                 return;
00662         }
00663         if (!*args) {SyntaxError();return;}
00664         char * arg1=StripArg(args);
00665         StripSpaces(args);
00666         if (!*args) {SyntaxError();return;}
00667         char * arg2=StripArg(args);
00668         StripSpaces(args);
00669         if (*args) {SyntaxError();return;}
00670         char* slash = strrchr(arg1,'\\');
00671         Bit32u size;Bit16u date;Bit16u time;Bit8u attr;
00672         char name[DOS_NAMELENGTH_ASCII], lname[LFN_NAMELENGTH+1], tname1[LFN_NAMELENGTH+1], tname2[LFN_NAMELENGTH+1], text1[LFN_NAMELENGTH+1], text2[LFN_NAMELENGTH+1], tfull[CROSS_LEN+2];
00673         //dir_source and target are introduced for when we support multiple files being renamed.
00674         char sargs[CROSS_LEN], targs[CROSS_LEN], dir_source[DOS_PATHLENGTH + 4] = {0}, dir_target[CROSS_LEN + 4] = {0}, target[CROSS_LEN + 4] = {0}; //not sure if drive portion is included in pathlength
00675 
00676         if (!slash) slash = strrchr(arg1,':');
00677         if (slash) {
00678                 /* If directory specified (crystal caves installer)
00679                  * rename from c:\X : rename c:\abc.exe abc.shr. 
00680                  * File must appear in C:\ 
00681                  * Ren X:\A\B C => ren X:\A\B X:\A\C */
00682                  
00683                 //Copy first and then modify, makes GCC happy
00684                 safe_strncpy(dir_source,arg1,DOS_PATHLENGTH + 4);
00685                 char* dummy = strrchr(dir_source,'\\');
00686                 if (!dummy) dummy = strrchr(dir_source,':');
00687                 if (!dummy) { //Possible due to length
00688                         WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
00689                         return;
00690                 }
00691                 dummy++;
00692                 *dummy = 0;
00693                 if (strchr(arg2,'\\')||strchr(arg2,':')) {
00694                         safe_strncpy(dir_target,arg2,DOS_PATHLENGTH + 4);
00695                         dummy = strrchr(dir_target,'\\');
00696                         if (!dummy) dummy = strrchr(dir_target,':');
00697                         if (dummy) {
00698                                 dummy++;
00699                                 *dummy = 0;
00700                                 if (strcasecmp(dir_source, dir_target)) {
00701                                         WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
00702                                         return;
00703                                 }
00704                         }
00705                         arg2=strrchr(arg2,strrchr(arg2,'\\')?'\\':':')+1;
00706                 }
00707                 if (strlen(dummy)&&dummy[strlen(dummy)-1]==':')
00708                         strcat(dummy, ".\\");
00709         } else {
00710                 if (strchr(arg2,'\\')||strchr(arg2,':')) {SyntaxError();return;};
00711                 strcpy(dir_source, ".\\");
00712         }
00713         
00714         strcpy(target,arg2);
00715 
00716         char path[DOS_PATHLENGTH], spath[DOS_PATHLENGTH], pattern[DOS_PATHLENGTH], full[DOS_PATHLENGTH], *r;
00717         if (!DOS_Canonicalize(arg1,full)) return;
00718         r=strrchr(full, '\\');
00719         if (r!=NULL) {
00720                 *r=0;
00721                 strcpy(path, full);
00722                 strcat(path, "\\");
00723                 strcpy(pattern, r+1);
00724                 *r='\\';
00725         } else {
00726                 strcpy(path, "");
00727                 strcpy(pattern, full);
00728         }
00729         int k=0;
00730         for (int i=0;i<(int)strlen(pattern);i++)
00731                 if (pattern[i]!='\"')
00732                         pattern[k++]=pattern[i];
00733         pattern[k]=0;
00734         strcpy(spath, path);
00735         if (strchr(arg1,'\"')||uselfn) {
00736                 if (!DOS_GetSFNPath(("\""+std::string(path)+"\\").c_str(), spath, false)) strcpy(spath, path);
00737                 if (!strlen(spath)||spath[strlen(spath)-1]!='\\') strcat(spath, "\\");
00738         }
00739         RealPt save_dta=dos.dta();
00740         dos.dta(dos.tables.tempdta);
00741         DOS_DTA dta(dos.dta());
00742         std::string pfull=std::string(spath)+std::string(pattern);
00743         int fbak=lfn_filefind_handle;
00744         lfn_filefind_handle=uselfn?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE;
00745         if (!DOS_FindFirst((char *)((uselfn&&pfull.length()&&pfull[0]!='"'?"\"":"")+pfull+(uselfn&&pfull.length()&&pfull[pfull.length()-1]!='"'?"\"":"")).c_str(), strchr(arg1,'*')!=NULL || strchr(arg1,'?')!=NULL ? 0xffff & ~DOS_ATTR_VOLUME & ~DOS_ATTR_DIRECTORY : 0xffff & ~DOS_ATTR_VOLUME)) {
00746                 lfn_filefind_handle=fbak;
00747                 WriteOut(MSG_Get("SHELL_CMD_RENAME_ERROR"),arg1);
00748         } else {
00749                 std::vector<std::string> sources;
00750                 sources.clear();
00751         
00752                 do {    /* File name and extension */
00753                         dta.GetResult(name,lname,size,date,time,attr);
00754                         lfn_filefind_handle=fbak;
00755 
00756                         if(!(attr&DOS_ATTR_DIRECTORY && (!strcmp(name, ".") || !strcmp(name, "..")))) {
00757                                 strcpy(dir_target, target);
00758                                 removeChar(dir_target, '\"');
00759                                 arg2=dir_target;
00760                                 strcpy(sargs, dir_source);
00761                                 if (uselfn) removeChar(sargs, '\"');
00762                                 strcat(sargs, uselfn?lname:name);
00763                                 if (uselfn&&strchr(arg2,'*')&&!strchr(arg2,'.')) strcat(arg2, ".*");
00764                                 char *dot1=strrchr(uselfn?lname:name,'.'), *dot2=strrchr(arg2,'.'), *star;
00765                                 if (dot2==NULL) {
00766                                         star=strchr(arg2,'*');
00767                                         if (strchr(arg2,'?')) {
00768                                                 for (unsigned int i=0; i<(uselfn?LFN_NAMELENGTH:DOS_NAMELENGTH) && i<(star?star-arg2:strlen(arg2)); i++) {
00769                                                         if (*(arg2+i)=='?'&&i<strlen(name))
00770                                                                 *(arg2+i)=name[i];
00771                                                 }
00772                                         }
00773                                         if (star) {
00774                                                 if (star-arg2<(unsigned int)strlen(name))
00775                                                         strcpy(star, name+(star-arg2));
00776                                                 else
00777                                                         *star=0;
00778                                         }
00779                                         removeChar(arg2, '?');
00780                                 } else {
00781                                         if (dot1) {
00782                                                 *dot1=0;
00783                                                 strcpy(tname1, uselfn?lname:name);
00784                                                 *dot1='.';
00785                                         } else
00786                                                 strcpy(tname1, uselfn?lname:name);
00787                                         *dot2=0;
00788                                         strcpy(tname2, arg2);
00789                                         *dot2='.';
00790                                         star=strchr(tname2,'*');
00791                                         if (strchr(tname2,'?')) {
00792                                                 for (unsigned int i=0; i<(uselfn?LFN_NAMELENGTH:DOS_NAMELENGTH) && i<(star?star-tname2:strlen(tname2)); i++) {
00793                                                         if (*(tname2+i)=='?'&&i<strlen(tname1))
00794                                                                 *(tname2+i)=tname1[i];
00795                                                 }
00796                                         }
00797                                         if (star) {
00798                                                 if (star-tname2<(unsigned int)strlen(tname1))
00799                                                         strcpy(star, tname1+(star-tname2));
00800                                                 else
00801                                                         *star=0;
00802                                         }
00803                                         removeChar(tname2, '?');
00804                                         if (dot1) {
00805                                                 strcpy(text1, dot1+1);
00806                                                 strcpy(text2, dot2+1);
00807                                                 star=strchr(text2,'*');
00808                                                 if (strchr(text2,'?')) {
00809                                                         for (unsigned int i=0; i<(uselfn?LFN_NAMELENGTH:DOS_NAMELENGTH) && i<(star?star-text2:strlen(text2)); i++) {
00810                                                                 if (*(text2+i)=='?'&&i<strlen(text1))
00811                                                                         *(text2+i)=text1[i];
00812                                                         }
00813                                                 }
00814                                                 if (star) {
00815                                                         if (star-text2<(unsigned int)strlen(text1))
00816                                                                 strcpy(star, text1+(star-text2));
00817                                                         else
00818                                                                 *star=0;
00819                                                 }
00820                                         } else {
00821                                                 strcpy(text2, dot2+1);
00822                                                 if (strchr(text2,'?')||strchr(text2,'*')) {
00823                                                         for (unsigned int i=0; i<(uselfn?LFN_NAMELENGTH:DOS_NAMELENGTH) && i<(star?star-text2:strlen(text2)); i++) {
00824                                                                 if (*(text2+i)=='*') {
00825                                                                         *(text2+i)=0;
00826                                                                         break;
00827                                                                 }
00828                                                         }
00829                                                 }
00830                                         }
00831                                         removeChar(text2, '?');
00832                                         strcpy(tfull, tname2);
00833                                         strcat(tfull, ".");
00834                                         strcat(tfull, text2);
00835                                         arg2=tfull;
00836                                 }
00837                                 strcpy(targs, dir_source);
00838                                 if (uselfn) removeChar(targs, '\"');
00839                                 strcat(targs, arg2);
00840                                 sources.push_back(uselfn?((sargs[0]!='"'?"\"":"")+std::string(sargs)+(sargs[strlen(sargs)-1]!='"'?"\"":"")).c_str():sargs);
00841                                 sources.push_back(uselfn?((targs[0]!='"'?"\"":"")+std::string(targs)+(targs[strlen(targs)-1]!='"'?"\"":"")).c_str():targs);
00842                                 sources.push_back(strlen(sargs)>2&&sargs[0]=='.'&&sargs[1]=='\\'?sargs+2:sargs);
00843                         }
00844                         lfn_filefind_handle=uselfn?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE;
00845                 } while ( DOS_FindNext() );
00846                 lfn_filefind_handle=fbak;
00847                 if (sources.empty()) WriteOut(MSG_Get("SHELL_CMD_RENAME_ERROR"),arg1);
00848                 else {
00849                         for (std::vector<std::string>::iterator source = sources.begin(); source != sources.end(); ++source) {
00850                                 char *oname=(char *)source->c_str();
00851                                 source++;
00852                                 if (source==sources.end()) break;
00853                                 char *nname=(char *)source->c_str();
00854                                 source++;
00855                                 if (source==sources.end()||oname==NULL||nname==NULL) break;
00856                                 char *fname=(char *)source->c_str();
00857                                 if (!DOS_Rename(oname,nname)&&fname!=NULL)
00858                                         WriteOut(MSG_Get("SHELL_CMD_RENAME_ERROR"),fname);
00859                         }
00860                 }
00861         }
00862         dos.dta(save_dta);
00863 }
00864 
00865 void DOS_Shell::CMD_ECHO(char * args){
00866         if (!*args) {
00867                 if (echo) { WriteOut(MSG_Get("SHELL_CMD_ECHO_ON"));}
00868                 else { WriteOut(MSG_Get("SHELL_CMD_ECHO_OFF"));}
00869                 return;
00870         }
00871         char buffer[512];
00872         char* pbuffer = buffer;
00873         safe_strncpy(buffer,args,512);
00874         StripSpaces(pbuffer);
00875         if (strcasecmp(pbuffer,"OFF")==0) {
00876                 echo=false;             
00877                 return;
00878         }
00879         if (strcasecmp(pbuffer,"ON")==0) {
00880                 echo=true;              
00881                 return;
00882         }
00883         if(strcasecmp(pbuffer,"/?")==0) { HELP("ECHO"); }
00884 
00885         args++;//skip first character. either a slash or dot or space
00886         size_t len = strlen(args); //TODO check input of else ook nodig is.
00887         if(len && args[len - 1] == '\r') {
00888                 LOG(LOG_MISC,LOG_WARN)("Hu ? carriage return already present. Is this possible?");
00889                 WriteOut("%s\n",args);
00890         } else WriteOut("%s\r\n",args);
00891 }
00892 
00893 
00894 void DOS_Shell::CMD_EXIT(char * args) {
00895         HELP("EXIT");
00896         exit = true;
00897 }
00898 
00899 void DOS_Shell::CMD_CHDIR(char * args) {
00900         HELP("CHDIR");
00901         StripSpaces(args);
00902         char sargs[CROSS_LEN];
00903         if (*args && !DOS_GetSFNPath(args,sargs,false)) {
00904                 WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
00905                 return;
00906         }
00907         Bit8u drive = DOS_GetDefaultDrive()+'A';
00908         char dir[DOS_PATHLENGTH];
00909         if (!*args) {
00910         DOS_GetCurrentDir(0,dir,true);
00911                 WriteOut("%c:\\%s\n",drive,dir);
00912         } else if(strlen(args) == 2 && args[1]==':') {
00913                 Bit8u targetdrive = (args[0] | 0x20)-'a' + 1;
00914                 unsigned char targetdisplay = *reinterpret_cast<unsigned char*>(&args[0]);
00915         if(!DOS_GetCurrentDir(targetdrive,dir,true)) { // verify that this should be true
00916                         if(drive == 'Z') {
00917                                 WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_NOT_FOUND"),toupper(targetdisplay));
00918                         } else {
00919                                 WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
00920                         }
00921                         return;
00922                 }
00923                 WriteOut("%c:\\%s\n",toupper(targetdisplay),dir);
00924                 if(drive == 'Z')
00925                         WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT"),toupper(targetdisplay));
00926         } else if (!DOS_ChangeDir(sargs)) {
00927                 /* Changedir failed. Check if the filename is longer then 8 and/or contains spaces */
00928            
00929                 std::string temps(args),slashpart;
00930                 std::string::size_type separator = temps.find_first_of("\\/");
00931                 if(!separator) {
00932                         slashpart = temps.substr(0,1);
00933                         temps.erase(0,1);
00934                 }
00935         separator = temps.find_first_of("\"");
00936         if(separator != std::string::npos) temps.erase(separator);
00937                 separator = temps.rfind('.');
00938                 if(separator != std::string::npos) temps.erase(separator);
00939                 separator = temps.find(' ');
00940                 if(separator != std::string::npos) {/* Contains spaces */
00941                         temps.erase(separator);
00942                         if(temps.size() >6) temps.erase(6);
00943                         temps += "~1";
00944                         WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT_2"),temps.insert(0,slashpart).c_str());
00945                 } else {
00946                         if (drive == 'Z') {
00947                                 WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT_3"));
00948                         } else {
00949                                 WriteOut(MSG_Get("SHELL_CMD_CHDIR_ERROR"),args);
00950                         }
00951                 }
00952         }
00953 }
00954 
00955 void DOS_Shell::CMD_MKDIR(char * args) {
00956         HELP("MKDIR");
00957         StripSpaces(args);
00958         char * rem=ScanCMDRemain(args);
00959         if (rem) {
00960                 WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
00961                 return;
00962         }
00963         if (!*args) {
00964                 WriteOut(MSG_Get("SHELL_MISSING_PARAMETER"));
00965                 return;
00966         }
00967         if (!DOS_MakeDir(args)) {
00968                 WriteOut(MSG_Get("SHELL_CMD_MKDIR_ERROR"),args);
00969         }
00970 }
00971 
00972 void DOS_Shell::CMD_RMDIR(char * args) {
00973         HELP("RMDIR");
00974         // ignore /s,and /q switches for compatibility
00975         ScanCMDBool(args,"S");
00976         ScanCMDBool(args,"Q");
00977         StripSpaces(args);
00978         char * rem=ScanCMDRemain(args);
00979         if (rem) {
00980                 WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
00981                 return;
00982         }
00983         if (!*args) {
00984                 WriteOut(MSG_Get("SHELL_MISSING_PARAMETER"));
00985                 return;
00986         }
00987         if (!DOS_RemoveDir(args)) {
00988                 WriteOut(MSG_Get("SHELL_CMD_RMDIR_ERROR"),args);
00989         }
00990 }
00991 
00992 static void FormatNumber(Bit64u num,char * buf) {
00993         Bit32u numm,numk,numb,numg,numt;
00994         numb=num % 1000;
00995         num/=1000;
00996         numk=num % 1000;
00997         num/=1000;
00998         numm=num % 1000;
00999         num/=1000;
01000         numg=num % 1000;
01001         num/=1000;
01002         numt=num;
01003         if (numt) {
01004                 sprintf(buf,"%u%c%03u%c%03u%c%03u%c%03u",numt,dos.tables.country[7],numg,dos.tables.country[7],numm,dos.tables.country[7],numk,dos.tables.country[7],numb);
01005                 return;
01006         }
01007         if (numg) {
01008                 sprintf(buf,"%u%c%03u%c%03u%c%03u",numg,dos.tables.country[7],numm,dos.tables.country[7],numk,dos.tables.country[7],numb);
01009                 return;
01010         }
01011         if (numm) {
01012                 sprintf(buf,"%u%c%03u%c%03u",numm,dos.tables.country[7],numk,dos.tables.country[7],numb);
01013                 return;
01014         }
01015         if (numk) {
01016                 sprintf(buf,"%u%c%03u",numk,dos.tables.country[7],numb);
01017                 return;
01018         }
01019         sprintf(buf,"%u",numb);
01020 }
01021 
01022 char buffer[15] = {0};
01023 char *FormatDate(Bit16u year, Bit8u month, Bit8u day) {
01024         char formatstring[6], c=dos.tables.country[11];
01025         sprintf(formatstring, dos.tables.country[0]==1?"D%cM%cY":(dos.tables.country[0]==2?"Y%cM%cD":"M%cD%cY"), c, c);
01026         Bitu bufferptr=0;
01027         for(Bitu i = 0; i < 5; i++) {
01028                 if(i==1 || i==3) {
01029                         buffer[bufferptr] = formatstring[i];
01030                         bufferptr++;
01031                 } else {
01032                         if(formatstring[i]=='M') bufferptr += (Bitu)sprintf(buffer+bufferptr,"%02u", month);
01033                         if(formatstring[i]=='D') bufferptr += (Bitu)sprintf(buffer+bufferptr,"%02u", day);
01034                         if(formatstring[i]=='Y') bufferptr += (Bitu)sprintf(buffer+bufferptr,"%04u", year);
01035                 }
01036         }
01037         return buffer;
01038 }
01039 
01040 char *FormatTime(Bitu hour, Bitu min, Bitu sec, Bitu msec)      {
01041         Bitu fhour=hour;
01042         static char retBuf[14];
01043         char ampm[3]="";
01044         if (!(dos.tables.country[17]&1)) {                                                                                      // 12 hour notation?
01045                 if (hour!=12)
01046                         hour %= 12;
01047                 strcpy(ampm, hour != 12 && hour == fhour ? "am" : "pm");
01048         }
01049         char sep = dos.tables.country[13];
01050         if (sec>=100&&msec>=100)
01051                 sprintf(retBuf, "%2u%c%02u%c", (unsigned int)hour, sep, (unsigned int)min, *ampm);
01052         else
01053                 sprintf(retBuf, "%u%c%02u%c%02u%c%02u%s", (unsigned int)hour, sep, (unsigned int)min, sep, (unsigned int)sec, dos.tables.country[9], (unsigned int)msec, ampm);
01054         return retBuf;
01055         }
01056 
01057 
01058 struct DtaResult {
01059         char name[DOS_NAMELENGTH_ASCII];
01060         char lname[LFN_NAMELENGTH+1];
01061         Bit32u size;
01062         Bit16u date;
01063         Bit16u time;
01064         Bit8u attr;
01065 
01066         static bool groupDef(const DtaResult &lhs, const DtaResult &rhs) { return (lhs.attr & DOS_ATTR_DIRECTORY) && !(rhs.attr & DOS_ATTR_DIRECTORY)?true:((((lhs.attr & DOS_ATTR_DIRECTORY) && (rhs.attr & DOS_ATTR_DIRECTORY)) || (!(lhs.attr & DOS_ATTR_DIRECTORY) && !(rhs.attr & DOS_ATTR_DIRECTORY))) && strcmp(lhs.name, rhs.name) < 0); }
01067         static bool groupDirs(const DtaResult &lhs, const DtaResult &rhs) { return (lhs.attr & DOS_ATTR_DIRECTORY) && !(rhs.attr & DOS_ATTR_DIRECTORY); }
01068         static bool compareName(const DtaResult &lhs, const DtaResult &rhs) { return strcmp(lhs.name, rhs.name) < 0; }
01069         static bool compareExt(const DtaResult &lhs, const DtaResult &rhs) { return strcmp(lhs.getExtension(), rhs.getExtension()) < 0; }
01070         static bool compareSize(const DtaResult &lhs, const DtaResult &rhs) { return lhs.size < rhs.size; }
01071         static bool compareDate(const DtaResult &lhs, const DtaResult &rhs) { return lhs.date < rhs.date || (lhs.date == rhs.date && lhs.time < rhs.time); }
01072 
01073         const char * getExtension() const {
01074                 const char * ext = empty_string;
01075                 if (name[0] != '.') {
01076                         ext = strrchr(name, '.');
01077                         if (!ext) ext = empty_string;
01078                 }
01079                 return ext;
01080         }
01081 
01082 };
01083 
01084 Bit32u byte_count,file_count,dir_count;
01085 Bitu p_count;
01086 std::vector<std::string> dirs, adirs;
01087 
01088 static bool dirPaused(DOS_Shell * shell, Bitu w_size, bool optP, bool optW) {
01089         p_count+=optW?5:1;
01090         if (optP && p_count%(GetPauseCount()*w_size)<1) {
01091                 shell->WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
01092                 Bit8u c;Bit16u n=1;
01093                 DOS_ReadFile(STDIN,&c,&n);
01094                 if (c==3) {shell->WriteOut("^C\r\n");return false;}
01095                 if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
01096         }
01097         return true;
01098 }
01099 
01100 static bool doDir(DOS_Shell * shell, char * args, DOS_DTA dta, char * numformat, Bitu w_size, bool optW, bool optZ, bool optS, bool optP, bool optB, bool optA, bool optAD, bool optAminusD, bool optAS, bool optAminusS, bool optAH, bool optAminusH, bool optAR, bool optAminusR, bool optAA, bool optAminusA, bool optO, bool optOG, bool optON, bool optOD, bool optOE, bool optOS, bool reverseSort) {
01101         char path[DOS_PATHLENGTH];
01102         char sargs[CROSS_LEN], largs[CROSS_LEN];
01103 
01104         /* Make a full path in the args */
01105         if (!DOS_Canonicalize(args,path)) {
01106                 shell->WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
01107                 return true;
01108         }
01109         *(strrchr(path,'\\')+1)=0;
01110         if (!DOS_GetSFNPath(path,sargs,false)) {
01111                 shell->WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
01112                 return true;
01113         }
01114     if (!optB&&!optS) {
01115                 shell->WriteOut(MSG_Get("SHELL_CMD_DIR_INTRO"),uselfn&&!optZ&&DOS_GetSFNPath(path,largs,true)?largs:sargs);
01116                 if (optP) {
01117                         p_count+=optW?10:2;
01118                         if (p_count%(GetPauseCount()*w_size)<2) {
01119                                 shell->WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
01120                                 Bit8u c;Bit16u n=1;
01121                                 DOS_ReadFile(STDIN,&c,&n);
01122                                 if (c==3) {shell->WriteOut("^C\r\n");return false;}
01123                                 if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
01124                         }
01125                 }
01126         }
01127     if (*(sargs+strlen(sargs)-1) != '\\') strcat(sargs,"\\");
01128 
01129         Bit32u cbyte_count=0,cfile_count=0,w_count=0;
01130         int fbak=lfn_filefind_handle;
01131         lfn_filefind_handle=uselfn&&!optZ?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE;
01132         bool ret=DOS_FindFirst(args,0xffff & ~DOS_ATTR_VOLUME), found=true, first=true;
01133         lfn_filefind_handle=fbak;
01134         if (ret) {
01135                 std::vector<DtaResult> results;
01136 
01137                 lfn_filefind_handle=uselfn&&!optZ?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE;
01138                 do {    /* File name and extension */
01139                         DtaResult result;
01140                         dta.GetResult(result.name,result.lname,result.size,result.date,result.time,result.attr);
01141 
01142                         /* Skip non-directories if option AD is present, or skip dirs in case of A-D */
01143                         if(optAD && !(result.attr&DOS_ATTR_DIRECTORY) ) continue;
01144                         else if(optAminusD && (result.attr&DOS_ATTR_DIRECTORY) ) continue;
01145                         else if(optAS && !(result.attr&DOS_ATTR_SYSTEM) ) continue;
01146                         else if(optAminusS && (result.attr&DOS_ATTR_SYSTEM) ) continue;
01147                         else if(optAH && !(result.attr&DOS_ATTR_HIDDEN) ) continue;
01148                         else if(optAminusH && (result.attr&DOS_ATTR_HIDDEN) ) continue;
01149                         else if(optAR && !(result.attr&DOS_ATTR_READ_ONLY) ) continue;
01150                         else if(optAminusR && (result.attr&DOS_ATTR_READ_ONLY) ) continue;
01151                         else if(optAA && !(result.attr&DOS_ATTR_ARCHIVE) ) continue;
01152                         else if(optAminusA && (result.attr&DOS_ATTR_ARCHIVE) ) continue;
01153                         else if(!(optA||optAD||optAminusD||optAS||optAminusS||optAH||optAminusH||optAR||optAminusR||optAA||optAminusA) && (result.attr&DOS_ATTR_SYSTEM || result.attr&DOS_ATTR_HIDDEN) && strcmp(result.name, "..") ) continue;
01154 
01155                         results.push_back(result);
01156 
01157                 } while ( (ret=DOS_FindNext()) );
01158                 lfn_filefind_handle=fbak;
01159 
01160                 if (optON) {
01161                         // Sort by name
01162                         std::sort(results.begin(), results.end(), DtaResult::compareName);
01163                 } else if (optOE) {
01164                         // Sort by extension
01165                         std::sort(results.begin(), results.end(), DtaResult::compareExt);
01166                 } else if (optOD) {
01167                         // Sort by date
01168                         std::sort(results.begin(), results.end(), DtaResult::compareDate);
01169                 } else if (optOS) {
01170                         // Sort by size
01171                         std::sort(results.begin(), results.end(), DtaResult::compareSize);
01172                 } else if (optOG) {
01173                         // Directories first, then files
01174                         std::sort(results.begin(), results.end(), DtaResult::groupDirs);
01175                 } else if (optO) {
01176                         // Directories first, then files, both sort by name
01177                         std::sort(results.begin(), results.end(), DtaResult::groupDef);
01178                 }
01179                 if (reverseSort) {
01180                         std::reverse(results.begin(), results.end());
01181                 }
01182 
01183                 for (std::vector<DtaResult>::iterator iter = results.begin(); iter != results.end(); ++iter) {
01184 
01185                         char * name = iter->name;
01186                         char *lname = iter->lname;
01187                         Bit32u size = iter->size;
01188                         Bit16u date = iter->date;
01189                         Bit16u time = iter->time;
01190                         Bit8u attr = iter->attr;
01191 
01192                         /* output the file */
01193                         if (optB) {
01194                                 // this overrides pretty much everything
01195                                 if (strcmp(".",uselfn&&!optZ?lname:name) && strcmp("..",uselfn&&!optZ?lname:name)) {
01196                                         shell->WriteOut("%s\n",uselfn&&!optZ?lname:name);
01197                                 }
01198                         } else {
01199                                 if (first&&optS) {
01200                                         first=false;
01201                                         shell->WriteOut("\n");
01202                                         shell->WriteOut(MSG_Get("SHELL_CMD_DIR_INTRO"),uselfn&&!optZ&&DOS_GetSFNPath(path,largs,true)?largs:sargs);
01203                                         if (optP) {
01204                                                 p_count+=optW?15:3;
01205                                                 if (optS&&p_count%(GetPauseCount()*w_size)<3) {
01206                                                         shell->WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
01207                                                         Bit8u c;Bit16u n=1;
01208                                                         DOS_ReadFile(STDIN,&c,&n);
01209                                                         if (c==3) {shell->WriteOut("^C\r\n");return false;}
01210                                                         if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
01211                                                 }
01212                                         }
01213                                 }
01214                                 char * ext = empty_string;
01215                                 if (!optW && (name[0] != '.')) {
01216                                         ext = strrchr(name, '.');
01217                                         if (!ext) ext = empty_string;
01218                                         else *ext++ = 0;
01219                                 }
01220                                 Bit8u day       = (Bit8u)(date & 0x001f);
01221                                 Bit8u month     = (Bit8u)((date >> 5) & 0x000f);
01222                                 Bit16u year = (Bit16u)((date >> 9) + 1980);
01223                                 Bit8u hour      = (Bit8u)((time >> 5 ) >> 6);
01224                                 Bit8u minute = (Bit8u)((time >> 5) & 0x003f);
01225 
01226                                 if (attr & DOS_ATTR_DIRECTORY) {
01227                                         if (optW) {
01228                                                 shell->WriteOut("[%s]",name);
01229                                                 size_t namelen = strlen(name);
01230                                                 if (namelen <= 14) {
01231                                                         for (size_t i=14-namelen;i>0;i--) shell->WriteOut(" ");
01232                                                 }
01233                                         } else {
01234                                                 shell->WriteOut("%-8s %-3s   %-16s %s %s %s\n",name,ext,"<DIR>",FormatDate(year,month,day),FormatTime(hour,minute,100,100),uselfn&&!optZ?lname:"");
01235                                         }
01236                                         dir_count++;
01237                                 } else {
01238                                         if (optW) {
01239                                                 shell->WriteOut("%-16s",name);
01240                                         } else {
01241                                                 FormatNumber(size,numformat);
01242                                                 shell->WriteOut("%-8s %-3s   %16s %s %s %s\n",name,ext,numformat,FormatDate(year,month,day),FormatTime(hour,minute,100,100),uselfn&&!optZ?lname:"");
01243                                         }
01244                                         if (optS) {
01245                                                 cfile_count++;
01246                                                 cbyte_count+=size;
01247                                         }
01248                                         file_count++;
01249                                         byte_count+=size;
01250                                 }
01251                                 if (optW) w_count++;
01252                         }
01253                         if (optP && !(++p_count%(GetPauseCount()*w_size))) {
01254                                 if (optW&&w_count%5) {shell->WriteOut("\n");w_count=0;}
01255                                 shell->WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
01256                                 Bit8u c;Bit16u n=1;
01257                                 DOS_ReadFile(STDIN,&c,&n);
01258                                 if (c==3) {shell->WriteOut("^C\r\n");return false;}
01259                                 if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
01260                         }
01261                 }
01262 
01263                 if (!results.size())
01264                         found=false;
01265                 else if (optW&&w_count%5)
01266                         shell->WriteOut("\n");
01267         } else
01268                 found=false;
01269         if (!found&&!optB&&!optS) {
01270                 shell->WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args);
01271                 if (!dirPaused(shell, w_size, optP, optW)) return false;
01272         }
01273         if (optS) {
01274                 size_t len=strlen(sargs);
01275                 strcat(sargs, "*.*");
01276                 bool ret=DOS_FindFirst(sargs,0xffff & ~DOS_ATTR_VOLUME);
01277                 *(sargs+len)=0;
01278                 if (ret) {
01279                         std::vector<std::string> cdirs;
01280                         cdirs.clear();
01281                         do {    /* File name and extension */
01282                                 DtaResult result;
01283                                 dta.GetResult(result.name,result.lname,result.size,result.date,result.time,result.attr);
01284 
01285                                 if(result.attr&DOS_ATTR_DIRECTORY && strcmp(result.name, ".")&&strcmp(result.name, "..")) {
01286                                         strcat(sargs, result.name);
01287                                         strcat(sargs, "\\");
01288                                         char *fname = strrchr(args, '\\');
01289                                         if (fname==NULL) fname=args;
01290                                         else fname++;
01291                                         strcat(sargs, fname);
01292                                         cdirs.push_back((sargs[0]!='"'&&sargs[strlen(sargs)-1]=='"'?"\"":"")+std::string(sargs));
01293                                         *(sargs+len)=0;
01294                                 }
01295                         } while ( (ret=DOS_FindNext()) );
01296                         dirs.insert(dirs.begin()+1, cdirs.begin(), cdirs.end());
01297                 }
01298                 if (found&&!optB) {
01299                         FormatNumber(cbyte_count,numformat);
01300                         shell->WriteOut(MSG_Get("SHELL_CMD_DIR_BYTES_USED"),cfile_count,numformat);
01301                         if (!dirPaused(shell, w_size, optP, optW)) return false;
01302                 }
01303         }
01304         return true;
01305 }
01306 
01307 void DOS_Shell::CMD_DIR(char * args) {
01308         HELP("DIR");
01309         char numformat[16];
01310         char path[DOS_PATHLENGTH];
01311         char sargs[CROSS_LEN];
01312 
01313         std::string line;
01314         if(GetEnvStr("DIRCMD",line)){
01315                 std::string::size_type idx = line.find('=');
01316                 std::string value=line.substr(idx +1 , std::string::npos);
01317                 line = std::string(args) + " " + value;
01318                 args=const_cast<char*>(line.c_str());
01319         }
01320 
01321         ScanCMDBool(args,"4"); /* /4 ignored (default) */
01322         bool optW=ScanCMDBool(args,"W");
01323         bool optP=ScanCMDBool(args,"P");
01324         if (ScanCMDBool(args,"WP") || ScanCMDBool(args,"PW")) optW=optP=true;
01325         if (ScanCMDBool(args,"-W")) optW=false;
01326         if (ScanCMDBool(args,"-P")) optP=false;
01327         bool optZ=ScanCMDBool(args,"Z");
01328         if (ScanCMDBool(args,"-Z")) optZ=false;
01329         bool optS=ScanCMDBool(args,"S");
01330         if (ScanCMDBool(args,"-S")) optS=false;
01331         bool optB=ScanCMDBool(args,"B");
01332         if (ScanCMDBool(args,"-B")) optB=false;
01333         bool optA=ScanCMDBool(args,"A");
01334         bool optAD=ScanCMDBool(args,"AD")||ScanCMDBool(args,"A:D");
01335         bool optAminusD=ScanCMDBool(args,"A-D");
01336         bool optAS=ScanCMDBool(args,"AS")||ScanCMDBool(args,"A:S");
01337         bool optAminusS=ScanCMDBool(args,"A-S");
01338         bool optAH=ScanCMDBool(args,"AH")||ScanCMDBool(args,"A:H");
01339         bool optAminusH=ScanCMDBool(args,"A-H");
01340         bool optAR=ScanCMDBool(args,"AR")||ScanCMDBool(args,"A:R");
01341         bool optAminusR=ScanCMDBool(args,"A-R");
01342         bool optAA=ScanCMDBool(args,"AA")||ScanCMDBool(args,"A:A");
01343         bool optAminusA=ScanCMDBool(args,"A-A");
01344         if (ScanCMDBool(args,"-A")) {
01345                 optA = false;
01346                 optAD = false;
01347                 optAminusD = false;
01348                 optAS = false;
01349                 optAminusS = false;
01350                 optAH = false;
01351                 optAminusH = false;
01352                 optAR = false;
01353                 optAminusR = false;
01354                 optAA = false;
01355                 optAminusA = false;
01356         }
01357         // Sorting flags
01358         bool reverseSort = false;
01359         bool optON=ScanCMDBool(args,"ON")||ScanCMDBool(args,"O:N");
01360         if (ScanCMDBool(args,"O-N")) {
01361                 optON = true;
01362                 reverseSort = true;
01363         }
01364         bool optOD=ScanCMDBool(args,"OD")||ScanCMDBool(args,"O:D");
01365         if (ScanCMDBool(args,"O-D")) {
01366                 optOD = true;
01367                 reverseSort = true;
01368         }
01369         bool optOE=ScanCMDBool(args,"OE")||ScanCMDBool(args,"O:E");
01370         if (ScanCMDBool(args,"O-E")) {
01371                 optOE = true;
01372                 reverseSort = true;
01373         }
01374         bool optOS=ScanCMDBool(args,"OS")||ScanCMDBool(args,"O:S");
01375         if (ScanCMDBool(args,"O-S")) {
01376                 optOS = true;
01377                 reverseSort = true;
01378         }
01379         bool optOG=ScanCMDBool(args,"OG")||ScanCMDBool(args,"O:G");
01380         if (ScanCMDBool(args,"O-G")) {
01381                 optOG = true;
01382                 reverseSort = true;
01383         }
01384         bool optO=ScanCMDBool(args,"O");
01385         if (ScanCMDBool(args,"OGN")) optO=true;
01386         if (ScanCMDBool(args,"-O")) {
01387                 optO = false;
01388                 optOG = false;
01389                 optON = false;
01390                 optOD = false;
01391                 optOE = false;
01392                 optOS = false;
01393                 reverseSort = false;
01394         }
01395         char * rem=ScanCMDRemain(args);
01396         if (rem) {
01397                 WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
01398                 return;
01399         }
01400         byte_count=0;file_count=0;dir_count=0;p_count=0;
01401         Bitu w_size = optW?5:1;
01402 
01403         char buffer[CROSS_LEN];
01404         args = trim(args);
01405         size_t argLen = strlen(args);
01406         if (argLen == 0) {
01407                 strcpy(args,"*.*"); //no arguments.
01408         } else {
01409                 switch (args[argLen-1])
01410                 {
01411                 case '\\':      // handle \, C:\, etc.
01412                 case ':' :      // handle C:, etc.
01413                         strcat(args,"*.*");
01414                         break;
01415                 default:
01416                         break;
01417                 }
01418         }
01419         args = ExpandDot(args,buffer,CROSS_LEN);
01420 
01421         if (DOS_FindDevice(args) != DOS_DEVICES) {
01422                 WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args);
01423                 return;
01424         }
01425         if (!strrchr(args,'*') && !strrchr(args,'?')) {
01426                 Bit16u attribute=0;
01427                 if(!DOS_GetSFNPath(args,sargs,false)) {
01428                         WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
01429                         return;
01430                 }
01431                 if(DOS_GetFileAttr(sargs,&attribute) && (attribute&DOS_ATTR_DIRECTORY) ) {
01432                         DOS_FindFirst(sargs,0xffff & ~DOS_ATTR_VOLUME);
01433                         DOS_DTA dta(dos.dta());
01434                         strcpy(args,sargs);
01435                         strcat(args,"\\*.*");   // if no wildcard and a directory, get its files
01436                 }
01437         }
01438         if (!DOS_GetSFNPath(args,sargs,false)) {
01439                 WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
01440                 return;
01441         }
01442         if (!(uselfn&&!optZ&&strchr(sargs,'*'))&&!strrchr(sargs,'.'))
01443                 strcat(sargs,".*");     // if no extension, get them all
01444     sprintf(args,"\"%s\"",sargs);
01445 
01446         /* Make a full path in the args */
01447         if (!DOS_Canonicalize(args,path)) {
01448                 WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
01449                 return;
01450         }
01451         *(strrchr(path,'\\')+1)=0;
01452         if (!DOS_GetSFNPath(path,sargs,true)) {
01453                 WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
01454                 return;
01455         }
01456     if (*(sargs+strlen(sargs)-1) != '\\') strcat(sargs,"\\");
01457     if (!optB) {
01458                 if (strlen(sargs)>2&&sargs[1]==':') {
01459                         char c[]=" _:";
01460                         c[1]=toupper(sargs[0]);
01461                         CMD_VOL(c[1]>='A'&&c[1]<='Z'?c:empty_string);
01462                 } else
01463                         CMD_VOL(empty_string);
01464                 if (optP) p_count+=optW?15:3;
01465         }
01466 
01467         /* Command uses dta so set it to our internal dta */
01468         RealPt save_dta=dos.dta();
01469         dos.dta(dos.tables.tempdta);
01470         DOS_DTA dta(dos.dta());
01471         dirs.clear();
01472         dirs.push_back(std::string(args));
01473         while (!dirs.empty()) {
01474                 if (!doDir(this, (char *)dirs.begin()->c_str(), dta, numformat, w_size, optW, optZ, optS, optP, optB, optA, optAD, optAminusD, optAS, optAminusS, optAH, optAminusH, optAR, optAminusR, optAA, optAminusA, optO, optOG, optON, optOD, optOE, optOS, reverseSort)) {dos.dta(save_dta);return;}
01475                 dirs.erase(dirs.begin());
01476         }
01477         if (!optB) {
01478                 if (optS) {
01479                         WriteOut("\n");
01480                         if (!dirPaused(this, w_size, optP, optW)) {dos.dta(save_dta);return;}
01481                         if (!file_count&&!dir_count)
01482                                 WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args);
01483                         else
01484                                 WriteOut(MSG_Get("SHELL_CMD_DIR_FILES_LISTED"));
01485                         if (!dirPaused(this, w_size, optP, optW)) {dos.dta(save_dta);return;}
01486                 }
01487                 /* Show the summary of results */
01488                 FormatNumber(byte_count,numformat);
01489                 WriteOut(MSG_Get("SHELL_CMD_DIR_BYTES_USED"),file_count,numformat);
01490                 if (!dirPaused(this, w_size, optP, optW)) {dos.dta(save_dta);return;}
01491                 Bit8u drive=dta.GetSearchDrive();
01492                 //TODO Free Space
01493                 Bitu free_space=1024u*1024u*100u;
01494                 if (Drives[drive]) {
01495                         Bit32u bytes_sector32;Bit32u sectors_cluster32;Bit32u total_clusters32;Bit32u free_clusters32;
01496                         if ((dos.version.major > 7 || (dos.version.major == 7 && dos.version.minor >= 10)) &&
01497                                 Drives[drive]->AllocationInfo32(&bytes_sector32,&sectors_cluster32,&total_clusters32,&free_clusters32)) { /* FAT32 aware extended API */
01498                                 rsize=true;
01499                                 freec=0;
01500                                 free_space=(Bitu)bytes_sector32 * (Bitu)sectors_cluster32 * (Bitu)(freec?freec:free_clusters32);
01501                                 rsize=false;
01502                         }
01503                         else {
01504                                 Bit16u bytes_sector;Bit8u sectors_cluster;Bit16u total_clusters;Bit16u free_clusters;
01505                                 rsize=true;
01506                                 freec=0;
01507                                 Drives[drive]->AllocationInfo(&bytes_sector,&sectors_cluster,&total_clusters,&free_clusters);
01508                                 free_space=(Bitu)bytes_sector * (Bitu)sectors_cluster * (Bitu)(freec?freec:free_clusters);
01509                                 rsize=false;
01510                         }
01511                 }
01512                 FormatNumber(free_space,numformat);
01513                 WriteOut(MSG_Get("SHELL_CMD_DIR_BYTES_FREE"),dir_count,numformat);
01514                 if (!dirPaused(this, w_size, optP, optW)) {dos.dta(save_dta);return;}
01515         }
01516         dos.dta(save_dta);
01517 }
01518 
01519 void DOS_Shell::CMD_LS(char *args) {
01520         HELP("LS");
01521         bool optA=ScanCMDBool(args,"A");
01522         bool optL=ScanCMDBool(args,"L");
01523         bool optP=ScanCMDBool(args,"P");
01524         bool optZ=ScanCMDBool(args,"Z");
01525         char * rem=ScanCMDRemain(args);
01526         if (rem) {
01527                 WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
01528                 return;
01529         }
01530 
01531         RealPt save_dta=dos.dta();
01532         dos.dta(dos.tables.tempdta);
01533         DOS_DTA dta(dos.dta());
01534 
01535         std::string pattern = args;
01536         trim(pattern);
01537 
01538         const char last_char = (pattern.length() > 0 ? pattern.back() : '\0');
01539         switch (last_char) {
01540                 case '\0': // No arguments, search for all.
01541                         pattern = "*.*";
01542                         break;
01543                 case '\\': // Handle \, C:\, etc.
01544                 case ':':  // Handle C:, etc.
01545                         pattern += "*.*";
01546                         break;
01547                 default: break;
01548         }
01549 
01550         // Handle patterns starting with a dot.
01551         char buffer[CROSS_LEN];
01552         pattern = ExpandDot((char *)pattern.c_str(), buffer, sizeof(buffer));
01553 
01554         // When there's no wildcard and target is a directory then search files
01555         // inside the directory.
01556         const char *p = pattern.c_str();
01557         if (!strrchr(p, '*') && !strrchr(p, '?')) {
01558                 uint16_t attr = 0;
01559                 if (DOS_GetFileAttr(p, &attr) && (attr & DOS_ATTR_DIRECTORY))
01560                         pattern += "\\*.*";
01561         }
01562 
01563         // If no extension, list all files.
01564         // This makes patterns like foo* work.
01565         if (!strrchr(pattern.c_str(), '.'))
01566                 pattern += ".*";
01567 
01568         char spattern[CROSS_LEN];
01569         if (!DOS_GetSFNPath(pattern.c_str(),spattern,false)) {
01570                 WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
01571                 return;
01572         }
01573         int fbak=lfn_filefind_handle;
01574         lfn_filefind_handle=uselfn?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE;
01575         bool ret = DOS_FindFirst((char *)((uselfn?"\"":"")+std::string(spattern)+(uselfn?"\"":"")).c_str(), 0xffff & ~DOS_ATTR_VOLUME);
01576         if (!ret) {
01577                 lfn_filefind_handle=fbak;
01578                 if (trim(args))
01579                         WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"), trim(args));
01580                 else
01581                         WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
01582                 dos.dta(save_dta);
01583                 return;
01584         }
01585 
01586         std::vector<DtaResult> results;
01587         // reserve space for as many as we can fit into a single memory page
01588         // nothing more to it; make it larger if necessary
01589         results.reserve(MEM_PAGE_SIZE / sizeof(DtaResult));
01590 
01591         do {
01592                 DtaResult result;
01593                 dta.GetResult(result.name, result.lname, result.size, result.date, result.time, result.attr);
01594                 results.push_back(result);
01595         } while ((ret = DOS_FindNext()) == true);
01596         lfn_filefind_handle=fbak;
01597 
01598         size_t w_count, p_count, col;
01599         unsigned int max[10], total, tcols=real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
01600         if (!tcols) tcols=80;
01601 
01602         for (col=10; col>0; col--) {
01603                 for (int i=0; i<10; i++) max[i]=2;
01604                 if (optL) col=1;
01605                 if (col==1) break;
01606                 w_count=0;
01607                 for (const auto &entry : results) {
01608                         std::string name = uselfn&&!optZ?entry.lname:entry.name;
01609                         if (name == "." || name == "..") continue;
01610                         if (!optA && (entry.attr&DOS_ATTR_SYSTEM || entry.attr&DOS_ATTR_HIDDEN)) continue;
01611                         if (name.size()+2>max[w_count%col]) max[w_count%col]=(unsigned int)(name.size()+2);
01612                         ++w_count;
01613                 }
01614                 total=0;
01615                 for (size_t i=0; i<col; i++) total+=max[i];
01616                 if (total<tcols) break;
01617         }
01618         
01619         w_count = 0, p_count = 0;
01620 
01621         for (const auto &entry : results) {
01622                 std::string name = uselfn&&!optZ?entry.lname:entry.name;
01623                 if (name == "." || name == "..") continue;              
01624                 if (!optA && (entry.attr&DOS_ATTR_SYSTEM || entry.attr&DOS_ATTR_HIDDEN)) continue;
01625                 if (entry.attr & DOS_ATTR_DIRECTORY) {
01626                         if (!uselfn||optZ) upcase(name);
01627                         if (col==1) {
01628                                 WriteOut("\033[34;1m%s\033[0m\n", name.c_str());
01629                                 p_count++;
01630                         } else
01631                                 WriteOut("\033[34;1m%-*s\033[0m", max[w_count % col], name.c_str());
01632                 } else {
01633                         if (!uselfn||optZ) lowcase(name);
01634                         const bool is_executable = name.length()>4 && (!strcasecmp(name.substr(name.length()-4).c_str(), ".exe") || !strcasecmp(name.substr(name.length()-4).c_str(), ".com") || !strcasecmp(name.substr(name.length()-4).c_str(), ".bat"));
01635                         if (col==1) {
01636                                 WriteOut(is_executable?"\033[32;1m%s\033[0m\n":"%s\n", name.c_str());
01637                                 p_count++;
01638                         } else
01639                                 WriteOut(is_executable?"\033[32;1m%-*s\033[0m":"%-*s", max[w_count % col], name.c_str());
01640                 }
01641                 if (col>1) {
01642                         ++w_count;
01643                         if (w_count % col == 0) {p_count++;WriteOut_NoParsing("\n");}
01644                 }
01645                 if (optP&&p_count>=GetPauseCount()) {
01646                         WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
01647                         Bit8u c;Bit16u n=1;
01648                         DOS_ReadFile(STDIN,&c,&n);
01649                         if (c==3) {WriteOut("^C\r\n");dos.dta(save_dta);return;}
01650                         if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
01651                         p_count=0;
01652                 }
01653         }
01654         if (col>1&&w_count%col) WriteOut_NoParsing("\n");
01655         dos.dta(save_dta);
01656 }
01657 
01658 struct copysource {
01659         std::string filename;
01660         bool concat;
01661         copysource(std::string& filein,bool concatin):
01662                 filename(filein),concat(concatin){ };
01663         copysource():filename(""),concat(false){ };
01664 };
01665 
01666 
01667 void DOS_Shell::CMD_COPY(char * args) {
01668         HELP("COPY");
01669         static std::string defaulttarget = ".";
01670         StripSpaces(args);
01671         /* Command uses dta so set it to our internal dta */
01672         RealPt save_dta=dos.dta();
01673         dos.dta(dos.tables.tempdta);
01674         DOS_DTA dta(dos.dta());
01675         Bit32u size;Bit16u date;Bit16u time;Bit8u attr;
01676         char name[DOS_NAMELENGTH_ASCII], lname[LFN_NAMELENGTH+1];
01677         std::vector<copysource> sources;
01678         // ignore /b and /t switches: always copy binary
01679         while(ScanCMDBool(args,"B")) ;
01680         while(ScanCMDBool(args,"T")) ; //Shouldn't this be A ?
01681         while(ScanCMDBool(args,"A")) ;
01682         bool optY=ScanCMDBool(args,"Y");
01683         std::string line;
01684         if(GetEnvStr("COPYCMD",line)){
01685                 std::string::size_type idx = line.find('=');
01686                 std::string value=line.substr(idx +1 , std::string::npos);
01687                 char copycmd[CROSS_LEN];
01688                 strcpy(copycmd, value.c_str());
01689                 if (ScanCMDBool(copycmd, "Y") && !ScanCMDBool(copycmd, "-Y")) optY = true;
01690         }
01691         if (ScanCMDBool(args,"-Y")) optY=false;
01692         ScanCMDBool(args,"V");
01693 
01694         char * rem=ScanCMDRemain(args);
01695         if (rem) {
01696                 WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
01697                 dos.dta(save_dta);
01698                 return;
01699         }
01700         // Gather all sources (extension to copy more then 1 file specified at command line)
01701         // Concatenating files go as follows: All parts except for the last bear the concat flag.
01702         // This construction allows them to be counted (only the non concat set)
01703         char q[]="\"";
01704         char* source_p = NULL;
01705         char source_x[DOS_PATHLENGTH+CROSS_LEN];
01706         while ( (source_p = StripArg(args)) && *source_p ) {
01707                 do {
01708                         char* plus = strchr(source_p,'+');
01709                         // If StripWord() previously cut at a space before a plus then
01710                         // set concatenate flag on last source and remove leading plus.
01711                         if (plus == source_p && sources.size()) {
01712                                 sources[sources.size()-1].concat = true;
01713                                 // If spaces also followed plus then item is only a plus.
01714                                 if (strlen(++source_p)==0) break;
01715                                 plus = strchr(source_p,'+');
01716                         }
01717                         if (plus) {
01718                                 char *c=source_p+strlen(source_p)-1;
01719                                 if (*source_p=='"'&&*c=='"') {
01720                                         *c=0;
01721                                         if (strchr(source_p+1,'"'))
01722                                                 *plus++ = 0;
01723                                         else
01724                                                 plus=NULL;
01725                                         *c='"';
01726                                 } else
01727                                         *plus++ = 0;
01728                         }
01729                         safe_strncpy(source_x,source_p,CROSS_LEN);
01730                         bool has_drive_spec = false;
01731                         size_t source_x_len = strlen(source_x);
01732                         if (source_x_len>0) {
01733                                 if (source_x[source_x_len-1]==':') has_drive_spec = true;
01734                                 else if (uselfn&&strchr(source_x, '*')) {
01735                                         char * find_last;
01736                                         find_last=strrchr(source_x,'\\');
01737                                         if (find_last==NULL) find_last=source_x;
01738                                         else find_last++;
01739                                         if (strlen(find_last)>0&&source_x[source_x_len-1]=='*'&&strchr(find_last, '.')==NULL) strcat(source_x, ".*");
01740                                 }
01741                         }
01742                         if (!has_drive_spec  && !strpbrk(source_p,"*?") ) { //doubt that fu*\*.* is valid
01743                 char spath[DOS_PATHLENGTH];
01744                 if (DOS_GetSFNPath(source_p,spath,false)) {
01745                                         bool root=false;
01746                                         if (strlen(spath)==3&&spath[1]==':'&&spath[2]=='\\') {
01747                                                 root=true;
01748                                                 strcat(spath, "*.*");
01749                                         }
01750                                         if (DOS_FindFirst(spath,0xffff & ~DOS_ATTR_VOLUME)) {
01751                     dta.GetResult(name,lname,size,date,time,attr);
01752                                         if (attr & DOS_ATTR_DIRECTORY || root)
01753                                                 strcat(source_x,"\\*.*");
01754                                         }
01755                                 }
01756                         }
01757             std::string source_xString = std::string(source_x);
01758                         sources.push_back(copysource(source_xString,(plus)?true:false));
01759                         source_p = plus;
01760                 } while(source_p && *source_p);
01761         }
01762         // At least one source has to be there
01763         if (!sources.size() || !sources[0].filename.size()) {
01764                 WriteOut(MSG_Get("SHELL_MISSING_PARAMETER"));
01765                 dos.dta(save_dta);
01766                 return;
01767         }
01768 
01769         copysource target;
01770         // If more then one object exists and last target is not part of a 
01771         // concat sequence then make it the target.
01772         if(sources.size()>1 && !sources[sources.size()-2].concat){
01773                 target = sources.back();
01774                 sources.pop_back();
01775         }
01776         //If no target => default target with concat flag true to detect a+b+c
01777         if(target.filename.size() == 0) target = copysource(defaulttarget,true);
01778 
01779         copysource oldsource;
01780         copysource source;
01781         Bit32u count = 0;
01782         while(sources.size()) {
01783                 /* Get next source item and keep track of old source for concat start end */
01784                 oldsource = source;
01785                 source = sources[0];
01786                 sources.erase(sources.begin());
01787 
01788                 //Skip first file if doing a+b+c. Set target to first file
01789                 if(!oldsource.concat && source.concat && target.concat) {
01790                         target = source;
01791                         continue;
01792                 }
01793 
01794                 /* Make a full path in the args */
01795                 char pathSourcePre[DOS_PATHLENGTH], pathSource[DOS_PATHLENGTH+2];
01796                 char pathTarget[DOS_PATHLENGTH];
01797 
01798                 if (!DOS_Canonicalize(const_cast<char*>(source.filename.c_str()),pathSourcePre)) {
01799                         WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
01800                         dos.dta(save_dta);
01801                         return;
01802                 }
01803                 strcpy(pathSource,pathSourcePre);
01804                 if (uselfn) sprintf(pathSource,"\"%s\"",pathSourcePre);
01805                 // cut search pattern
01806                 char* pos = strrchr(pathSource,'\\');
01807                 if (pos) *(pos+1) = 0;
01808 
01809                 if (!DOS_Canonicalize(const_cast<char*>(target.filename.c_str()),pathTarget)) {
01810                         WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
01811                         dos.dta(save_dta);
01812                         return;
01813                 }
01814                 char* temp = strstr(pathTarget,"*.*");
01815                 if(temp && (temp == pathTarget || temp[-1] == '\\')) *temp = 0;//strip off *.* from target
01816         
01817                 // add '\\' if target is a directory
01818                 bool target_is_file = true;
01819                 if (pathTarget[strlen(pathTarget)-1]!='\\') {
01820                         if (DOS_FindFirst(pathTarget,0xffff & ~DOS_ATTR_VOLUME)) {
01821                                 dta.GetResult(name,lname,size,date,time,attr);
01822                                 if (attr & DOS_ATTR_DIRECTORY) {
01823                                         strcat(pathTarget,"\\");
01824                                         target_is_file = false;
01825                                 }
01826                         }
01827                 } else target_is_file = false;
01828 
01829                 //Find first sourcefile
01830                 char sPath[DOS_PATHLENGTH];
01831                 bool ret=DOS_GetSFNPath(source.filename.c_str(),sPath,false) && DOS_FindFirst((char *)((strchr(sPath, ' ')&&sPath[0]!='"'&&sPath[0]!='"'?"\"":"")+std::string(sPath)+(strchr(sPath, ' ')&&sPath[strlen(sPath)-1]!='"'?"\"":"")).c_str(),0xffff & ~DOS_ATTR_VOLUME);
01832                 if (!ret) {
01833                         WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),const_cast<char*>(source.filename.c_str()));
01834                         dos.dta(save_dta);
01835                         return;
01836                 }
01837 
01838                 Bit16u sourceHandle,targetHandle = 0;
01839                 char nameTarget[DOS_PATHLENGTH];
01840                 char nameSource[DOS_PATHLENGTH], nametmp[DOS_PATHLENGTH+2];
01841                 
01842                 // Cache so we don't have to recalculate
01843                 size_t pathTargetLen = strlen(pathTarget);
01844                 
01845                 // See if we have to substitute filename or extension
01846                 char *ext = 0;
01847                 size_t replacementOffset = 0;
01848                 if (pathTarget[pathTargetLen-1]!='\\') { 
01849                                 // only if it's not a directory
01850                         ext = strchr(pathTarget, '.');
01851                         if (ext > pathTarget) { // no possible substitution
01852                                 if (ext[-1] == '*') {
01853                                         // we substitute extension, save it, hide the name
01854                                         ext[-1] = 0;
01855                                         assert(ext > pathTarget + 1); // pathTarget is fully qualified
01856                                         if (ext[-2] != '\\') {
01857                                                 // there is something before the asterisk
01858                                                 // save the offset in the source names
01859 
01860                                                 replacementOffset = source.filename.find('*');
01861                                                 size_t lastSlash = source.filename.rfind('\\');
01862                                                 if (std::string::npos == lastSlash)
01863                                                         lastSlash = 0;
01864                                                 else
01865                                                         lastSlash++;
01866                                                 if (std::string::npos == replacementOffset
01867                                                           || replacementOffset < lastSlash) {
01868                                                         // no asterisk found or in wrong place, error
01869                                                         WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
01870                                                         dos.dta(save_dta);
01871                                                         return;
01872                                                 }
01873                                                 replacementOffset -= lastSlash;
01874 //                                              WriteOut("[II] replacement offset is %d\n", replacementOffset);
01875                                         }
01876                                 }
01877                                 if (ext[1] == '*') {
01878                                         // we substitute name, clear the extension
01879                                         *ext = 0;
01880                                 } else if (ext[-1]) {
01881                                         // we don't substitute anything, clear up
01882                                         ext = 0;
01883                                 }
01884                         }
01885                 }
01886 
01887                 bool echo=dos.echo, second_file_of_current_source = false;
01888                 while (ret) {
01889                         dta.GetResult(name,lname,size,date,time,attr);
01890 
01891                         if ((attr & DOS_ATTR_DIRECTORY)==0) {
01892                 Bit16u ftime,fdate;
01893 
01894                                 strcpy(nameSource,pathSource);
01895                                 strcat(nameSource,name);
01896                                 
01897                                 // Open Source
01898                                 if (DOS_OpenFile(nameSource,0,&sourceHandle)) {
01899                     // record the file date/time
01900                     bool ftdvalid = DOS_GetFileDate(sourceHandle, &ftime, &fdate);
01901                     if (!ftdvalid) LOG_MSG("WARNING: COPY cannot obtain file date/time");
01902 
01903                                         // Create Target or open it if in concat mode
01904                                         strcpy(nameTarget,q);
01905                     strcat(nameTarget,pathTarget);
01906                                         
01907                                         if (ext) { // substitute parts if necessary
01908                                                 if (!ext[-1]) { // substitute extension
01909                                                         strcat(nameTarget, (uselfn?lname:name) + replacementOffset);
01910                                                         char *p=strchr(nameTarget, '.');
01911                                                         strcpy(p==NULL?nameTarget+strlen(nameTarget):p, ext);
01912                                                 }
01913                                                 if (ext[1] == '*') { // substitute name (so just add the extension)
01914                                                         strcat(nameTarget, strchr(uselfn?lname:name, '.'));
01915                                                 }
01916                                         }
01917                                         
01918                     if (nameTarget[strlen(nameTarget)-1]=='\\') strcat(nameTarget,uselfn?lname:name);
01919                     strcat(nameTarget,q);
01920 
01921                                         //Special variable to ensure that copy * a_file, where a_file is not a directory concats.
01922                                         bool special = second_file_of_current_source && target_is_file && strchr(target.filename.c_str(), '*')==NULL;
01923                                         second_file_of_current_source = true; 
01924                                         if (special) oldsource.concat = true;
01925                                         if (*nameSource&&*nameTarget) {
01926                                                 strcpy(nametmp, nameSource[0]!='\"'&&nameTarget[0]=='\"'?"\"":"");
01927                                                 strcat(nametmp, nameSource);
01928                                                 strcat(nametmp, nameSource[strlen(nameSource)-1]!='\"'&&nameTarget[strlen(nameTarget)-1]=='\"'?"\"":"");
01929                                         } else
01930                                                 strcpy(nametmp, nameSource);
01931                                         if (!oldsource.concat && (!strcasecmp(nameSource, nameTarget) || !strcasecmp(nametmp, nameTarget)))
01932                                                 {
01933                                                 WriteOut("File cannot be copied onto itself\r\n");
01934                                                 dos.dta(save_dta);
01935                                                 DOS_CloseFile(sourceHandle);
01936                                                 if (targetHandle)
01937                                                         DOS_CloseFile(targetHandle);
01938                                                 return;
01939                                                 }
01940                                         Bit16u fattr;
01941                                         bool exist = DOS_GetFileAttr(nameTarget, &fattr);
01942                                         if (!(attr & DOS_ATTR_DIRECTORY) && DOS_FindDevice(nameTarget) == DOS_DEVICES) {
01943                                                 if (exist && !optY && !oldsource.concat) {
01944                                                         dos.echo=false;
01945                                                         WriteOut(MSG_Get("SHELL_CMD_COPY_CONFIRM"), nameTarget);
01946                                                         Bit8u c;
01947                                                         Bit16u n=1;
01948                                                         while (true)
01949                                                                 {
01950                                                                 DOS_ReadFile (STDIN,&c,&n);
01951                                                                 if (c==3) {WriteOut("^C\r\n");dos.dta(save_dta);DOS_CloseFile(sourceHandle);dos.echo=echo;return;}
01952                                                                 if (c=='y'||c=='Y') {WriteOut("Y\r\n", c);break;}
01953                                                                 if (c=='n'||c=='N') {WriteOut("N\r\n", c);break;}
01954                                                                 if (c=='a'||c=='A') {WriteOut("A\r\n", c);optY=true;break;}
01955                                                                 }
01956                                                         if (c=='n'||c=='N') {DOS_CloseFile(sourceHandle);ret = DOS_FindNext();continue;}
01957                                                 }
01958                                                 if (!exist&&size) {
01959                                                         int drive=strlen(nameTarget)>1&&(nameTarget[1]==':'||nameTarget[2]==':')?(toupper(nameTarget[nameTarget[0]=='"'?1:0])-'A'):-1;
01960                                                         if (drive>=0&&Drives[drive]) {
01961                                                                 Bit16u bytes_sector;Bit8u sectors_cluster;Bit16u total_clusters;Bit16u free_clusters;
01962                                                                 rsize=true;
01963                                                                 freec=0;
01964                                                                 Drives[drive]->AllocationInfo(&bytes_sector,&sectors_cluster,&total_clusters,&free_clusters);
01965                                                                 rsize=false;
01966                                                                 if ((Bitu)bytes_sector * (Bitu)sectors_cluster * (Bitu)(freec?freec:free_clusters)<size) {
01967                                                                         WriteOut(MSG_Get("SHELL_CMD_COPY_NOSPACE"), uselfn?lname:name);
01968                                                                         DOS_CloseFile(sourceHandle);
01969                                                                         ret = DOS_FindNext();
01970                                                                         continue;
01971                                                                 }
01972                                                         }
01973                                                 }
01974                                         }
01975                                         //Don't create a new file when in concat mode
01976                                         if (oldsource.concat || DOS_CreateFile(nameTarget,0,&targetHandle)) {
01977                                                 Bit32u dummy=0;
01978 
01979                         if (DOS_FindDevice(name) == DOS_DEVICES && !DOS_SetFileDate(targetHandle, ftime, fdate))
01980                             LOG_MSG("WARNING: COPY unable to apply date/time to dest");
01981 
01982                                                 //In concat mode. Open the target and seek to the eof
01983                                                 if (!oldsource.concat || (DOS_OpenFile(nameTarget,OPEN_READWRITE,&targetHandle) && 
01984                                                                           DOS_SeekFile(targetHandle,&dummy,DOS_SEEK_END))) {
01985                                                         // Copy 
01986                                                         static Bit8u buffer[0x8000]; // static, otherwise stack overflow possible.
01987                                                         bool    failed = false;
01988                                                         Bit16u  toread = 0x8000;
01989                                                         bool iscon=DOS_FindDevice(name)==DOS_FindDevice("con");
01990                                                         if (iscon) dos.echo=true;
01991                                                         bool cont;
01992                                                         do {
01993                                                                 if (!DOS_ReadFile(sourceHandle,buffer,&toread)) failed=true;
01994                                                                 if (iscon)
01995                                                                         {
01996                                                                         if (dos.errorcode==77)
01997                                                                                 {
01998                                                                                 WriteOut("^C\r\n");
01999                                                                                 dos.dta(save_dta);
02000                                                                                 DOS_CloseFile(sourceHandle);
02001                                                                                 DOS_CloseFile(targetHandle);
02002                                                                                 if (!exist) DOS_UnlinkFile(nameTarget);
02003                                                                                 dos.echo=echo;
02004                                                                                 return;
02005                                                                                 }
02006                                                                         cont=true;
02007                                                                         for (int i=0;i<toread;i++)
02008                                                                                 if (buffer[i]==26)
02009                                                                                         {
02010                                                                                         toread=i;
02011                                                                                         cont=false;
02012                                                                                         break;
02013                                                                                         }
02014                                                                         if (!DOS_WriteFile(targetHandle,buffer,&toread)) failed=true;
02015                                                                         if (cont) toread=0x8000;
02016                                                                         }
02017                                                                 else
02018                                                                         {
02019                                                                         if (!DOS_WriteFile(targetHandle,buffer,&toread)) failed=true;
02020                                                                         cont=toread == 0x8000;
02021                                                                         }
02022                                                         } while (cont);
02023                                                         if (!DOS_CloseFile(sourceHandle)) failed=true;
02024                                                         if (!DOS_CloseFile(targetHandle)) failed=true;
02025                                                         if (failed)
02026                                 WriteOut(MSG_Get("SHELL_CMD_COPY_ERROR"),uselfn?lname:name);
02027                             else if (strcmp(name,lname)&&uselfn)
02028                                 WriteOut(" %s [%s]\n",lname,name);
02029                             else
02030                                 WriteOut(" %s\n",uselfn?lname:name);
02031                                                         if(!source.concat && !special) count++; //Only count concat files once
02032                                                 } else {
02033                                                         DOS_CloseFile(sourceHandle);
02034                                                         WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast<char*>(target.filename.c_str()));
02035                                                 }
02036                                         } else {
02037                                                 DOS_CloseFile(sourceHandle);
02038                                                 WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast<char*>(target.filename.c_str()));
02039                                         }
02040                                 } else WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast<char*>(source.filename.c_str()));
02041                         }
02042                         //On to the next file if the previous one wasn't a device
02043                         if ((attr&DOS_ATTR_DEVICE) == 0) ret = DOS_FindNext();
02044                         else ret = false;
02045                 }
02046         }
02047 
02048         WriteOut(MSG_Get("SHELL_CMD_COPY_SUCCESS"),count);
02049         dos.dta(save_dta);
02050         dos.echo=echo;
02051         Drives[DOS_GetDefaultDrive()]->EmptyCache();
02052 }
02053 
02054 /* NTS: WARNING, this function modifies the buffer pointed to by char *args */
02055 void DOS_Shell::CMD_SET(char * args) {
02056         std::string line;
02057 
02058         HELP("SET");
02059         StripSpaces(args);
02060 
02061         if (*args == 0) { /* "SET" by itself means to show the environment block */
02062                 Bitu count = GetEnvCount();
02063 
02064                 for (Bitu a = 0;a < count;a++) {
02065                         if (GetEnvNum(a,line))
02066                                 WriteOut("%s\n",line.c_str());                  
02067                 }
02068 
02069         }
02070         else {
02071                 char *p;
02072 
02073                 { /* parse arguments at the start */
02074                         char *pcheck = args;
02075 
02076                         while (*pcheck != 0 && (*pcheck == ' ' || *pcheck == '\t')) pcheck++;
02077                         if (*pcheck != 0 && strlen(pcheck) > 3 && (strncasecmp(pcheck,"/p ",3) == 0))
02078                                 E_Exit("Set /P is not supported. Use Choice!"); /* TODO: What is SET /P supposed to do? */
02079                 }
02080 
02081                 /* Most SET commands take the form NAME=VALUE */
02082                 p = strchr(args,'=');
02083                 if (p == NULL) {
02084                         /* SET <variable> without assignment prints the variable instead */
02085                         if (!GetEnvStr(args,line)) WriteOut(MSG_Get("SHELL_CMD_SET_NOT_SET"),args);
02086                         WriteOut("%s\n",line.c_str());
02087                 } else {
02088                         /* ASCIIZ snip the args string in two, so that args is C-string name of the variable,
02089                          * and "p" is C-string value of the variable */
02090                         *p++ = 0;
02091 
02092                         /* No parsing is needed. The command interpreter does the variable substitution for us */
02093                         if (!SetEnv(args,p)) {
02094                                 /* NTS: If Win95 is any example, the command interpreter expands the variables for us */
02095                                 WriteOut(MSG_Get("SHELL_CMD_SET_OUT_OF_SPACE"));
02096                         }
02097                 }
02098         }
02099 }
02100 
02101 void DOS_Shell::CMD_IF(char * args) {
02102         HELP("IF");
02103         StripSpaces(args,'=');
02104         bool has_not=false;
02105 
02106         while (strncasecmp(args,"NOT",3) == 0) {
02107                 if (!isspace(*reinterpret_cast<unsigned char*>(&args[3])) && (args[3] != '=')) break;
02108                 args += 3;      //skip text
02109                 //skip more spaces
02110                 StripSpaces(args,'=');
02111                 has_not = !has_not;
02112         }
02113 
02114         if(strncasecmp(args,"ERRORLEVEL",10) == 0) {
02115                 args += 10;     //skip text
02116                 //Strip spaces and ==
02117                 StripSpaces(args,'=');
02118                 char* word = StripWord(args);
02119                 if(!isdigit(*word)) {
02120                         WriteOut(MSG_Get("SHELL_CMD_IF_ERRORLEVEL_MISSING_NUMBER"));
02121                         return;
02122                 }
02123 
02124                 Bit8u n = 0;
02125                 do n = n * 10 + (*word - '0');
02126                 while (isdigit(*++word));
02127                 if(*word && !isspace(*word)) {
02128                         WriteOut(MSG_Get("SHELL_CMD_IF_ERRORLEVEL_INVALID_NUMBER"));
02129                         return;
02130                 }
02131                 /* Read the error code from DOS */
02132                 if ((dos.return_code>=n) ==(!has_not)) DoCommand(args);
02133                 return;
02134         }
02135 
02136         if(strncasecmp(args,"EXIST ",6) == 0) {
02137                 args += 6; //Skip text
02138                 StripSpaces(args);
02139                 char* word = StripArg(args);
02140                 if (!*word) {
02141                         WriteOut(MSG_Get("SHELL_CMD_IF_EXIST_MISSING_FILENAME"));
02142                         return;
02143                 }
02144 
02145                 {       /* DOS_FindFirst uses dta so set it to our internal dta */
02146                         char spath[DOS_PATHLENGTH], path[DOS_PATHLENGTH], pattern[DOS_PATHLENGTH], full[DOS_PATHLENGTH], *r;
02147                         if (!DOS_Canonicalize(word,full)) return;
02148                         r=strrchr(full, '\\');
02149                         if (r!=NULL) {
02150                                 *r=0;
02151                                 strcpy(path, full);
02152                                 strcat(path, "\\");
02153                                 strcpy(pattern, r+1);
02154                                 *r='\\';
02155                         } else {
02156                                 strcpy(path, "");
02157                                 strcpy(pattern, full);
02158                         }
02159                         int k=0;
02160                         for (int i=0;i<(int)strlen(pattern);i++)
02161                                 if (pattern[i]!='\"')
02162                                         pattern[k++]=pattern[i];
02163                         pattern[k]=0;
02164                         strcpy(spath, path);
02165                         if (strchr(word,'\"')||uselfn) {
02166                                 if (!DOS_GetSFNPath(("\""+std::string(path)+"\\").c_str(), spath, false)) strcpy(spath, path);
02167                                 if (!strlen(spath)||spath[strlen(spath)-1]!='\\') strcat(spath, "\\");
02168                         }
02169                         RealPt save_dta=dos.dta();
02170                         dos.dta(dos.tables.tempdta);
02171                         int fbak=lfn_filefind_handle;
02172                         lfn_filefind_handle=uselfn?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE;
02173                         std::string sfull=std::string(spath)+std::string(pattern);
02174                         bool ret=DOS_FindFirst((char *)((uselfn&&sfull.length()&&sfull[0]!='"'?"\"":"")+sfull+(uselfn&&sfull.length()&&sfull[sfull.length()-1]!='"'?"\"":"")).c_str(),0xffff & ~(DOS_ATTR_VOLUME|DOS_ATTR_DIRECTORY));
02175                         lfn_filefind_handle=fbak;
02176                         dos.dta(save_dta);
02177                         if (ret==(!has_not)) DoCommand(args);
02178                 }
02179                 return;
02180         }
02181 
02182         /* Normal if string compare */
02183 
02184         char* word1 = args;
02185         // first word is until space or =
02186         while (*args && !isspace(*reinterpret_cast<unsigned char*>(args)) && (*args != '='))
02187                 args++;
02188         char* end_word1 = args;
02189 
02190         // scan for =
02191         while (*args && (*args != '='))
02192                 args++;
02193         // check for ==
02194         if ((*args==0) || (args[1] != '=')) {
02195                 SyntaxError();
02196                 return;
02197         }
02198         args += 2;
02199         StripSpaces(args,'=');
02200 
02201         char* word2 = args;
02202         // second word is until space or =
02203         while (*args && !isspace(*reinterpret_cast<unsigned char*>(args)) && (*args != '='))
02204                 args++;
02205 
02206         if (*args) {
02207                 *end_word1 = 0;         // mark end of first word
02208                 *args++ = 0;            // mark end of second word
02209                 StripSpaces(args,'=');
02210 
02211                 if ((strcmp(word1,word2)==0)==(!has_not)) DoCommand(args);
02212         }
02213 }
02214 
02215 void DOS_Shell::CMD_GOTO(char * args) {
02216         HELP("GOTO");
02217         StripSpaces(args);
02218         if (!bf) return;
02219         if (*args==':') args++;
02220         //label ends at the first space
02221         char* non_space = args;
02222         while (*non_space) {
02223                 if((*non_space == ' ') || (*non_space == '\t')) 
02224                         *non_space = 0; 
02225                 else non_space++;
02226         }
02227         if (!*args) {
02228                 WriteOut(MSG_Get("SHELL_CMD_GOTO_MISSING_LABEL"));
02229                 return;
02230         }
02231         if (!bf->Goto(args)) {
02232                 WriteOut(MSG_Get("SHELL_CMD_GOTO_LABEL_NOT_FOUND"),args);
02233                 return;
02234         }
02235 }
02236 
02237 void DOS_Shell::CMD_SHIFT(char * args ) {
02238         HELP("SHIFT");
02239         if(bf) bf->Shift();
02240 }
02241 
02242 void DOS_Shell::CMD_TYPE(char * args) {
02243         HELP("TYPE");
02244                 
02245         // ignore /p /h and /t for compatibility
02246         ScanCMDBool(args,"P");
02247         ScanCMDBool(args,"H");
02248         ScanCMDBool(args,"T");
02249         StripSpaces(args);
02250         if (strcasecmp(args,"nul")==0) return;
02251         if (!*args) {
02252                 WriteOut(MSG_Get("SHELL_SYNTAXERROR"));
02253                 return;
02254         }
02255         Bit16u handle;
02256         char * word;
02257 nextfile:
02258         word=StripArg(args);
02259         if (!DOS_OpenFile(word,0,&handle)) {
02260                 WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),word);
02261                 return;
02262         }
02263         Bit8u c;Bit16u n=1;
02264         bool iscon=DOS_FindDevice(word)==DOS_FindDevice("con");
02265         while (n) {
02266                 DOS_ReadFile(handle,&c,&n);
02267                 if (n==0 || c==0x1a) break; // stop at EOF
02268                 if (iscon) {
02269                         if (c==3) {WriteOut("^C\r\n");break;}
02270                         else if (c==13) WriteOut("\r\n");
02271                 }
02272                 DOS_WriteFile(STDOUT,&c,&n);
02273         }
02274         DOS_CloseFile(handle);
02275         if (*args) goto nextfile;
02276 }
02277 
02278 void DOS_Shell::CMD_REM(char * args) {
02279         HELP("REM");
02280 }
02281 
02282 static char PAUSED(void) {
02283         Bit8u c; Bit16u n=1, handle;
02284         if (!usecon&&DOS_OpenFile("con", OPEN_READWRITE, &handle)) {
02285                 DOS_ReadFile (handle,&c,&n);
02286                 DOS_CloseFile(handle);
02287         } else
02288                 DOS_ReadFile (STDIN,&c,&n);
02289         return c;
02290 }
02291 
02292 void DOS_Shell::CMD_MORE(char * args) {
02293         HELP("MORE");
02294         //ScanCMDBool(args,">");
02295         int nchars = 0, nlines = 0, linecount = 0, LINES = 25, COLS = 80, TABSIZE = 8;
02296         char * word;
02297         Bit8u c, last=0;
02298         Bit16u n=1;
02299         StripSpaces(args);
02300         if (IS_PC98_ARCH) {
02301                 LINES=real_readb(0x60,0x113) & 0x01 ? 25 : 20;
02302                 COLS=80;
02303         } else {
02304                 LINES=real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
02305                 COLS=real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
02306         }
02307         LINES--;
02308         if(!*args||!strcasecmp(args, "con")) {
02309                 while (true) {
02310                         DOS_ReadFile (STDIN,&c,&n);
02311                         if (c==3) {WriteOut("^C\r\n");dos.echo=echo;return;}
02312                         else if (n==0) {if (last!=10) WriteOut("\r\n");dos.echo=echo;return;}
02313                         else if (c==13&&last==26) {dos.echo=echo;return;}
02314                         else {
02315                                 if (c==10);
02316                                 else if (c==13) {
02317                                         linecount++;
02318                                         WriteOut("\r\n");
02319                                 } else if (c=='\t') {
02320                                         do {
02321                                                 WriteOut(" ");
02322                                                 nchars++;
02323                                         } while ( nchars < COLS && nchars % TABSIZE );
02324                                 } else {
02325                                         nchars++;
02326                                         WriteOut("%c", c);
02327                                 }
02328                                 if (c == 13 || nchars >= COLS) {
02329                                         nlines++;
02330                                         nchars = 0;
02331                                         if (nlines == LINES) {
02332                                                 WriteOut("-- More -- (%u) --",linecount);
02333                                                 if (PAUSED()==3) {WriteOut("^C\r\n");return;}
02334                                                 WriteOut("\n");
02335                                                 nlines=0;
02336                                         }
02337                                 }
02338                                 last=c;
02339                         }
02340                 }
02341         }
02342         if (strcasecmp(args,"nul")==0) return;
02343         if (!*args) {
02344                 WriteOut(MSG_Get("SHELL_SYNTAXERROR"));
02345                 return;
02346         }
02347         Bit16u handle;
02348 nextfile:
02349         word=StripArg(args);
02350         if (!DOS_OpenFile(word,0,&handle)) {
02351                 WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),word);
02352                 return;
02353         }
02354         do {
02355                 n=1;
02356                 DOS_ReadFile(handle,&c,&n);
02357                 DOS_WriteFile(STDOUT,&c,&n);
02358                 if (c != '\t') nchars++;
02359                 else do {
02360                         WriteOut(" ");
02361                         nchars++;
02362                 } while ( nchars < COLS && nchars % TABSIZE );
02363 
02364                 if (c == '\n') linecount++;
02365                 if ((c == '\n') || (nchars >= COLS)) {
02366                         nlines++;
02367                         nchars = 0;
02368                         if (nlines == LINES) {
02369                                 WriteOut("-- More -- %s (%u) --",word,linecount);
02370                                 if (PAUSED()==3) {WriteOut("^C\r\n");return;}
02371                                 WriteOut("\n");
02372                                 nlines=0;
02373                         }
02374                 }
02375         } while (n);
02376         DOS_CloseFile(handle);
02377         if (*args) {
02378                 WriteOut("\n");
02379                 if (PAUSED()==3) {WriteOut("^C\r\n");return;}
02380                 goto nextfile;
02381         }
02382 }
02383 
02384 void DOS_Shell::CMD_PAUSE(char * args){
02385         HELP("PAUSE");
02386         if(args && *args) {
02387                 args++;
02388                 WriteOut("%s\n",args);  // optional specified message
02389         } else
02390         WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
02391         Bit8u c;Bit16u n=1;
02392         DOS_ReadFile(STDIN,&c,&n);
02393         if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
02394 }
02395 
02396 void DOS_Shell::CMD_CALL(char * args){
02397         HELP("CALL");
02398         this->call=true; /* else the old batchfile will be closed first */
02399         this->ParseLine(args);
02400         this->call=false;
02401 }
02402 
02403 void DOS_Shell::CMD_DATE(char * args) {
02404         HELP("DATE");   
02405         if(ScanCMDBool(args,"H")) {
02406                 // synchronize date with host parameter
02407                 time_t curtime;
02408                 struct tm *loctime;
02409                 curtime = time (NULL);
02410                 loctime = localtime (&curtime);
02411                 
02412                 reg_cx = loctime->tm_year+1900;
02413                 reg_dh = loctime->tm_mon+1;
02414                 reg_dl = loctime->tm_mday;
02415 
02416                 reg_ah=0x2b; // set system date
02417                 CALLBACK_RunRealInt(0x21);
02418                 return;
02419         }
02420         // check if a date was passed in command line
02421         char c=dos.tables.country[11], c1, c2;
02422         Bit32u newday,newmonth,newyear;
02423         int n=dos.tables.country[0]==1?sscanf(args,"%u%c%u%c%u",&newday,&c1,&newmonth,&c2,&newyear):(dos.tables.country[0]==2?sscanf(args,"%u%c%u%c%u",&newyear,&c1,&newmonth,&c2,&newday):sscanf(args,"%u%c%u%c%u",&newmonth,&c1,&newday,&c2,&newyear));
02424         if (n==5 && c1==c && c2==c) {
02425                 reg_cx = static_cast<Bit16u>(newyear);
02426                 reg_dh = static_cast<Bit8u>(newmonth);
02427                 reg_dl = static_cast<Bit8u>(newday);
02428 
02429                 reg_ah=0x2b; // set system date
02430                 CALLBACK_RunRealInt(0x21);
02431                 if(reg_al==0xff) WriteOut(MSG_Get("SHELL_CMD_DATE_ERROR"));
02432                 return;
02433         }
02434         // display the current date
02435         reg_ah=0x2a; // get system date
02436         CALLBACK_RunRealInt(0x21);
02437 
02438         const char* datestring = MSG_Get("SHELL_CMD_DATE_DAYS");
02439         Bit32u length;
02440         char day[6] = {0};
02441         if(sscanf(datestring,"%u",&length) && (length<5) && (strlen(datestring)==((size_t)length*7+1))) {
02442                 // date string appears valid
02443                 for(Bit32u i = 0; i < length; i++) day[i] = datestring[reg_al*length+1+i];
02444         }
02445         bool dateonly = ScanCMDBool(args,"T");
02446         if(!dateonly) WriteOut(MSG_Get("SHELL_CMD_DATE_NOW"));
02447         
02448         if(date_host_forced) {
02449                 time_t curtime;
02450 
02451                 struct tm *loctime;
02452                 curtime = time (NULL);
02453 
02454                 loctime = localtime (&curtime);
02455                 int hosty=loctime->tm_year+1900;
02456                 int hostm=loctime->tm_mon+1;
02457                 int hostd=loctime->tm_mday;
02458                 if (hostm == 1 || hostm == 2) hosty--;
02459                 hostm = (hostm + 9) % 12 + 1;
02460                 int y = hosty % 100;
02461                 int century = hosty / 100;
02462                 int week = ((13 * hostm - 1) / 5 + hostd + y + y/4 + century/4 - 2*century) % 7;
02463                 if (week < 0) week = (week + 7) % 7;
02464 
02465                 const char* my_week[7]={"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
02466                 WriteOut("%s %s\n",my_week[week],FormatDate((Bit16u)reg_cx, (Bit8u)reg_dh, (Bit8u)reg_dl));
02467         } else
02468                 WriteOut("%s %s\n",day, FormatDate((Bit16u)reg_cx, (Bit8u)reg_dh, (Bit8u)reg_dl));
02469         if(!dateonly) {
02470                 char format[11];
02471                 sprintf(format, dos.tables.country[0]==1?"DD%cMM%cYYYY":(dos.tables.country[0]==2?"YYYY%cMM%cDD":"MM%cDD%cYYYY"), c, c);
02472                 WriteOut(MSG_Get("SHELL_CMD_DATE_SETHLP"), format);
02473         }
02474 }
02475 
02476 void DOS_Shell::CMD_TIME(char * args) {
02477         HELP("TIME");
02478         if(ScanCMDBool(args,"H")) {
02479                 // synchronize time with host parameter
02480                 time_t curtime;
02481                 struct tm *loctime;
02482                 curtime = time (NULL);
02483                 loctime = localtime (&curtime);
02484                 
02485                 //reg_cx = loctime->;
02486                 //reg_dh = loctime->;
02487                 //reg_dl = loctime->;
02488 
02489                 // reg_ah=0x2d; // set system time TODO
02490                 // CALLBACK_RunRealInt(0x21);
02491                 
02492                 Bit32u ticks=(Bit32u)(((double)(loctime->tm_hour*3600+
02493                                                                                 loctime->tm_min*60+
02494                                                                                 loctime->tm_sec))*18.206481481);
02495                 mem_writed(BIOS_TIMER,ticks);
02496                 return;
02497         }
02498         Bit32u newhour,newminute,newsecond;
02499         char c=dos.tables.country[13], c1, c2;
02500         if (sscanf(args,"%u%c%u%c%u",&newhour,&c1,&newminute,&c2,&newsecond)==5 && c1==c && c2==c) {
02501                 //reg_ch = static_cast<Bit16u>(newhour);
02502                 //reg_cl = static_cast<Bit8u>(newminute);
02503                 //reg_dx = static_cast<Bit8u>(newsecond)<<8;
02504 
02505                 //reg_ah=0x2d; // set system time
02506                 //CALLBACK_RunRealInt(0x21);
02507                 //if(reg_al==0xff) WriteOut(MSG_Get("SHELL_CMD_TIME_ERROR"));
02508 
02509                 if( newhour > 23 || newminute > 59 || newsecond > 59)
02510                         WriteOut(MSG_Get("SHELL_CMD_TIME_ERROR"));
02511                 else {
02512                         Bit32u ticks=(Bit32u)(((double)(newhour*3600+
02513                                                                                         newminute*60+
02514                                                                                         newsecond))*18.206481481);
02515                         mem_writed(BIOS_TIMER,ticks);
02516                 }
02517                 return;
02518         }
02519         bool timeonly = ScanCMDBool(args,"T");
02520 
02521         reg_ah=0x2c; // get system time
02522         CALLBACK_RunRealInt(0x21);
02523 /*
02524                 reg_dl= // 1/100 seconds
02525                 reg_dh= // seconds
02526                 reg_cl= // minutes
02527                 reg_ch= // hours
02528 */
02529         if(timeonly) {
02530                 WriteOut("%2u:%02u\n",reg_ch,reg_cl);
02531         } else {
02532                 WriteOut(MSG_Get("SHELL_CMD_TIME_NOW"));
02533                 WriteOut("%s\n", FormatTime(reg_ch,reg_cl,reg_dh,reg_dl));
02534                 char format[9];
02535                 sprintf(format, "hh%cmm%css", dos.tables.country[13], dos.tables.country[13]);
02536                 WriteOut(MSG_Get("SHELL_CMD_TIME_SETHLP"), format);
02537         }
02538 }
02539 
02540 void DOS_Shell::CMD_SUBST(char * args) {
02541 /* If more that one type can be substed think of something else 
02542  * E.g. make basedir member dos_drive instead of localdrive
02543  */
02544         HELP("SUBST");
02545         try {
02546                 char mountstring[DOS_PATHLENGTH+CROSS_LEN+20];
02547                 strcpy(mountstring,"MOUNT ");
02548                 StripSpaces(args);
02549                 std::string arg;
02550                 CommandLine command(0,args);
02551                 if (!command.GetCount()) {
02552                         char name[DOS_NAMELENGTH_ASCII],lname[LFN_NAMELENGTH];
02553                         Bit32u size;Bit16u date;Bit16u time;Bit8u attr;
02554                         /* Command uses dta so set it to our internal dta */
02555                         RealPt save_dta = dos.dta();
02556                         dos.dta(dos.tables.tempdta);
02557                         DOS_DTA dta(dos.dta());
02558 
02559                         WriteOut(MSG_Get("SHELL_CMD_SUBST_DRIVE_LIST"));
02560                         WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_FORMAT"),"Drive","Type","Label");
02561                         for(int p = 0;p < 8;p++) WriteOut("----------");
02562 
02563                         for (int d = 0;d < DOS_DRIVES;d++) {
02564                                 if (!Drives[d]||strncmp(Drives[d]->GetInfo(),"local ",6)) continue;
02565 
02566                                 char root[7] = {(char)('A'+d),':','\\','*','.','*',0};
02567                                 bool ret = DOS_FindFirst(root,DOS_ATTR_VOLUME);
02568                                 if (ret) {
02569                                         dta.GetResult(name,lname,size,date,time,attr);
02570                                         DOS_FindNext(); //Mark entry as invalid
02571                                 } else name[0] = 0;
02572 
02573                                 /* Change 8.3 to 11.0 */
02574                                 const char* dot = strchr(name, '.');
02575                                 if(dot && (dot - name == 8) ) { 
02576                                         name[8] = name[9];name[9] = name[10];name[10] = name[11];name[11] = 0;
02577                                 }
02578 
02579                                 root[1] = 0; //This way, the format string can be reused.
02580                                 WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_FORMAT"),root, Drives[d]->GetInfo(),name);       
02581                         }
02582                         dos.dta(save_dta);
02583                         return;
02584                 }
02585 
02586                 if (command.GetCount() != 2) throw 0 ;
02587   
02588                 command.FindCommand(1,arg);
02589                 if( (arg.size()>1) && arg[1] !=':')  throw(0);
02590                 char temp_str[2] = { 0,0 };
02591                 temp_str[0]=(char)toupper(args[0]);
02592                 command.FindCommand(2,arg);
02593                 if((arg=="/D") || (arg=="/d")) {
02594                         if(!Drives[temp_str[0]-'A'] ) throw 1; //targetdrive not in use
02595                         strcat(mountstring,"-u ");
02596                         strcat(mountstring,temp_str);
02597                         this->ParseLine(mountstring);
02598                         return;
02599                 }
02600                 if(Drives[temp_str[0]-'A'] ) throw 2; //targetdrive in use
02601                 strcat(mountstring,temp_str);
02602                 strcat(mountstring," ");
02603 
02604         Bit8u drive;char dir[DOS_PATHLENGTH+2],fulldir[DOS_PATHLENGTH];
02605         if (strchr(arg.c_str(),'\"')==NULL)
02606             sprintf(dir,"\"%s\"",arg.c_str());
02607         else strcpy(dir,arg.c_str());
02608         if (!DOS_MakeName(dir,fulldir,&drive)) throw 3;
02609         
02610                 localDrive* ldp=0;
02611                 if( ( ldp=dynamic_cast<localDrive*>(Drives[drive])) == 0 ) throw 4;
02612                 char newname[CROSS_LEN];   
02613                 strcpy(newname, ldp->basedir);     
02614                 strcat(newname,fulldir);
02615                 CROSS_FILENAME(newname);
02616                 ldp->dirCache.ExpandName(newname);
02617                 strcat(mountstring,"\"");          
02618                 strcat(mountstring, newname);
02619                 strcat(mountstring,"\"");          
02620                 this->ParseLine(mountstring);
02621         }
02622         catch(int a){
02623                 switch (a) {
02624                         case 1:
02625                         WriteOut(MSG_Get("SHELL_CMD_SUBST_NO_REMOVE"));
02626                                 break;
02627                         case 2:
02628                                 WriteOut(MSG_Get("SHELL_CMD_SUBST_IN_USE"));
02629                                 break;
02630                         case 3:
02631                                 WriteOut(MSG_Get("SHELL_CMD_SUBST_INVALID_PATH"));
02632                                 break;
02633                         case 4:
02634                                 WriteOut(MSG_Get("SHELL_CMD_SUBST_NOT_LOCAL"));
02635                                 break;
02636                         default:
02637                                 WriteOut(MSG_Get("SHELL_CMD_SUBST_FAILURE"));
02638                 }
02639                 return;
02640         }
02641         catch(...) {            //dynamic cast failed =>so no localdrive
02642                 WriteOut(MSG_Get("SHELL_CMD_SUBST_FAILURE"));
02643                 return;
02644         }
02645    
02646         return;
02647 }
02648 
02649 void DOS_Shell::CMD_LOADHIGH(char *args){
02650         HELP("LOADHIGH");
02651         Bit16u umb_start=dos_infoblock.GetStartOfUMBChain();
02652         Bit8u umb_flag=dos_infoblock.GetUMBChainState();
02653         Bit8u old_memstrat=(Bit8u)(DOS_GetMemAllocStrategy()&0xff);
02654         if (umb_start==0x9fff) {
02655                 if ((umb_flag&1)==0) DOS_LinkUMBsToMemChain(1);
02656                 DOS_SetMemAllocStrategy(0x80);  // search in UMBs first
02657                 this->ParseLine(args);
02658                 Bit8u current_umb_flag=dos_infoblock.GetUMBChainState();
02659                 if ((current_umb_flag&1)!=(umb_flag&1)) DOS_LinkUMBsToMemChain(umb_flag);
02660                 DOS_SetMemAllocStrategy(old_memstrat);  // restore strategy
02661         } else this->ParseLine(args);
02662 }
02663 
02664 void DOS_Shell::CMD_CHOICE(char * args){
02665         HELP("CHOICE");
02666         static char defchoice[3] = {'y','n',0};
02667         char *rem = NULL, *ptr;
02668         bool optN = ScanCMDBool(args,"N");
02669         bool optS = ScanCMDBool(args,"S"); //Case-sensitive matching
02670         // ignore /b and /m switches for compatibility
02671         ScanCMDBool(args,"B");
02672         ScanCMDBool(args,"M"); // Text
02673         ScanCMDBool(args,"T"); //Default Choice after timeout
02674         if (args) {
02675                 char *last = strchr(args,0);
02676                 StripSpaces(args);
02677                 rem = ScanCMDRemain(args);
02678                 if (rem && *rem && (tolower(rem[1]) != 'c')) {
02679                         WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
02680                         return;
02681                 }
02682                 if (args == rem) args = strchr(rem,0)+1;
02683                 if (rem) rem += 2;
02684                 if(rem && rem[0]==':') rem++; /* optional : after /c */
02685                 if (args > last) args = NULL;
02686         }
02687         if (!rem || !*rem) rem = defchoice; /* No choices specified use YN */
02688         ptr = rem;
02689         Bit8u c;
02690         if(!optS) while ((c = (Bit8u)(*ptr))) *ptr++ = (char)toupper(c); /* When in no case-sensitive mode. make everything upcase */
02691         if(args && *args ) {
02692                 StripSpaces(args);
02693                 size_t argslen = strlen(args);
02694                 if(argslen>1 && args[0] == '"' && args[argslen-1] =='"') {
02695                         args[argslen-1] = 0; //Remove quotes
02696                         args++;
02697                 }
02698                 WriteOut(args);
02699         }
02700         /* Show question prompt of the form [a,b]? where a b are the choice values */
02701         if (!optN) {
02702                 if(args && *args) WriteOut(" ");
02703                 WriteOut("[");
02704                 size_t len = strlen(rem);
02705                 for(size_t t = 1; t < len; t++) {
02706                         WriteOut("%c,",rem[t-1]);
02707                 }
02708                 WriteOut("%c]?",rem[len-1]);
02709         }
02710 
02711         Bit16u n=1;
02712         do {
02713                 DOS_ReadFile (STDIN,&c,&n);
02714                 if (c==3) {WriteOut("^C\r\n");dos.return_code=0;return;}
02715         } while (!c || !(ptr = strchr(rem,(optS?c:toupper(c)))));
02716         c = optS?c:(Bit8u)toupper(c);
02717         DOS_WriteFile (STDOUT,&c, &n);
02718         c = '\n'; DOS_WriteFile (STDOUT,&c, &n);
02719         dos.return_code = (Bit8u)(ptr-rem+1);
02720 }
02721 
02722 static bool doAttrib(DOS_Shell * shell, char * args, DOS_DTA dta, bool optS, bool adda, bool adds, bool addh, bool addr, bool suba, bool subs, bool subh, bool subr) {
02723     char spath[DOS_PATHLENGTH],sargs[DOS_PATHLENGTH+4],path[DOS_PATHLENGTH+4],full[DOS_PATHLENGTH],sfull[DOS_PATHLENGTH+2];
02724         if (!DOS_Canonicalize(args,full)||strrchr(full,'\\')==NULL) { shell->WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));return false; }
02725         if (!DOS_GetSFNPath(args,spath,false)) {
02726                 shell->WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args);
02727                 return false;
02728         }
02729         if (!uselfn||!DOS_GetSFNPath(args,sfull,true)) strcpy(sfull,full);
02730     sprintf(sargs,"\"%s\"",spath);
02731     bool found=false, res=DOS_FindFirst(sargs,0xffff & ~DOS_ATTR_VOLUME);
02732         if (!res&&!optS) return false;
02733         //end can't be 0, but if it is we'll get a nice crash, who cares :)
02734         strcpy(path,full);
02735         *(strrchr(path,'\\')+1)=0;
02736         char * end=strrchr(full,'\\')+1;*end=0;
02737         char * lend=strrchr(sfull,'\\')+1;*lend=0;
02738     char name[DOS_NAMELENGTH_ASCII],lname[LFN_NAMELENGTH+1];
02739     Bit32u size;Bit16u time,date;Bit8u attr;Bit16u fattr;
02740         while (res) {
02741                 dta.GetResult(name,lname,size,date,time,attr);
02742                 if (!((!strcmp(name, ".") || !strcmp(name, "..") || strchr(sargs, '*')!=NULL || strchr(sargs, '?')!=NULL) && attr & DOS_ATTR_DIRECTORY)) {
02743                         found=true;
02744                         strcpy(end,name);
02745                         strcpy(lend,lname);
02746                         if (strlen(full)&&DOS_GetFileAttr(((uselfn||strchr(full, ' ')?(full[0]!='"'?"\"":""):"")+std::string(full)+(uselfn||strchr(full, ' ')?(full[strlen(full)-1]!='"'?"\"":""):"")).c_str(), &fattr)) {
02747                                 bool attra=fattr&DOS_ATTR_ARCHIVE, attrs=fattr&DOS_ATTR_SYSTEM, attrh=fattr&DOS_ATTR_HIDDEN, attrr=fattr&DOS_ATTR_READ_ONLY;
02748                                 if (adda||adds||addh||addr||suba||subs||subh||subr) {
02749                                         if (adda) fattr|=DOS_ATTR_ARCHIVE;
02750                                         if (adds) fattr|=DOS_ATTR_SYSTEM;
02751                                         if (addh) fattr|=DOS_ATTR_HIDDEN;
02752                                         if (addr) fattr|=DOS_ATTR_READ_ONLY;
02753                                         if (suba) fattr&=~DOS_ATTR_ARCHIVE;
02754                                         if (subs) fattr&=~DOS_ATTR_SYSTEM;
02755                                         if (subh) fattr&=~DOS_ATTR_HIDDEN;
02756                                         if (subr) fattr&=~DOS_ATTR_READ_ONLY;
02757                                         if (DOS_SetFileAttr(((uselfn||strchr(full, ' ')?(full[0]!='"'?"\"":""):"")+std::string(full)+(uselfn||strchr(full, ' ')?(full[strlen(full)-1]!='"'?"\"":""):"")).c_str(), fattr)) {
02758                                                 if (DOS_GetFileAttr(((uselfn||strchr(full, ' ')?(full[0]!='"'?"\"":""):"")+std::string(full)+(uselfn||strchr(full, ' ')?(full[strlen(full)-1]!='"'?"\"":""):"")).c_str(), &fattr))
02759                                                         shell->WriteOut("  %c  %c%c%c   %s\n", fattr&DOS_ATTR_ARCHIVE?'A':' ', fattr&DOS_ATTR_SYSTEM?'S':' ', fattr&DOS_ATTR_HIDDEN?'H':' ', fattr&DOS_ATTR_READ_ONLY?'R':' ', uselfn?sfull:full);
02760                                         } else
02761                                                 shell->WriteOut(MSG_Get("SHELL_CMD_ATTRIB_SET_ERROR"),uselfn?sfull:full);
02762                                 } else
02763                                         shell->WriteOut("  %c  %c%c%c   %s\n", attra?'A':' ', attrs?'S':' ', attrh?'H':' ', attrr?'R':' ', uselfn?sfull:full);
02764                         } else
02765                                 shell->WriteOut(MSG_Get("SHELL_CMD_ATTRIB_GET_ERROR"),uselfn?sfull:full);
02766                 }
02767                 res=DOS_FindNext();
02768         }
02769         if (optS) {
02770                 size_t len=strlen(path);
02771                 strcat(path, "*.*");
02772                 bool ret=DOS_FindFirst(path,0xffff & ~DOS_ATTR_VOLUME);
02773                 *(path+len)=0;
02774                 if (ret) {
02775                         std::vector<std::string> cdirs;
02776                         cdirs.clear();
02777                         do {    /* File name and extension */
02778                                 DtaResult result;
02779                                 dta.GetResult(result.name,result.lname,result.size,result.date,result.time,result.attr);
02780 
02781                                 if(result.attr&DOS_ATTR_DIRECTORY && strcmp(result.name, ".")&&strcmp(result.name, "..")) {
02782                                         strcat(path, result.name);
02783                                         strcat(path, "\\");
02784                                         char *fname = strrchr(args, '\\');
02785                                         if (fname!=NULL) fname++;
02786                                         else {
02787                                                 fname = strrchr(args, ':');
02788                                                 if (fname!=NULL) fname++;
02789                                                 else fname=args;
02790                                         }
02791                                         strcat(path, fname);
02792                                         cdirs.push_back((path[0]!='"'&&path[strlen(path)-1]=='"'?"\"":"")+std::string(path));
02793                                         *(path+len)=0;
02794                                 }
02795                         } while ( (ret=DOS_FindNext()) );
02796                         adirs.insert(adirs.begin()+1, cdirs.begin(), cdirs.end());
02797                 }
02798         }
02799         return found;
02800 }
02801 
02802 void DOS_Shell::CMD_ATTRIB(char *args){
02803         HELP("ATTRIB");
02804         StripSpaces(args);
02805 
02806         bool optS=ScanCMDBool(args,"S");
02807         char * rem=ScanCMDRemain(args);
02808         if (rem) {
02809                 WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
02810                 return;
02811         }
02812         
02813         bool adda=false, adds=false, addh=false, addr=false, suba=false, subs=false, subh=false, subr=false;
02814         char sfull[DOS_PATHLENGTH+2];
02815         char* arg1;
02816         strcpy(sfull, "*.*");
02817         do {
02818                 arg1=StripArg(args);
02819                 if (!strcasecmp(arg1, "+A")) adda=true;
02820                 else if (!strcasecmp(arg1, "+S")) adds=true;
02821                 else if (!strcasecmp(arg1, "+H")) addh=true;
02822                 else if (!strcasecmp(arg1, "+R")) addr=true;
02823                 else if (!strcasecmp(arg1, "-A")) suba=true;
02824                 else if (!strcasecmp(arg1, "-S")) subs=true;
02825                 else if (!strcasecmp(arg1, "-H")) subh=true;
02826                 else if (!strcasecmp(arg1, "-R")) subr=true;
02827                 else if (*arg1) {
02828                         strcpy(sfull, arg1);
02829                         if (uselfn&&strchr(sfull, '*')) {
02830                                 char * find_last;
02831                                 find_last=strrchr(sfull,'\\');
02832                                 if (find_last==NULL) find_last=sfull;
02833                                 else find_last++;
02834                                 if (sfull[strlen(sfull)-1]=='*'&&strchr(find_last, '.')==NULL) strcat(sfull, ".*");
02835                         }
02836                 }
02837         } while (*args);
02838 
02839         char buffer[CROSS_LEN];
02840         args = ExpandDot(sfull,buffer, CROSS_LEN);
02841         StripSpaces(args);
02842         RealPt save_dta=dos.dta();
02843         dos.dta(dos.tables.tempdta);
02844         DOS_DTA dta(dos.dta());
02845         adirs.clear();
02846         adirs.push_back(std::string(args));
02847         bool found=false;
02848         while (!adirs.empty()) {
02849                 if (doAttrib(this, (char *)adirs.begin()->c_str(), dta, optS, adda, adds, addh, addr, suba, subs, subh, subr))
02850                         found=true;
02851                 adirs.erase(adirs.begin());
02852         }
02853         if (!found) WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args);
02854         dos.dta(save_dta);
02855 }
02856 
02857 void DOS_Shell::CMD_PROMPT(char *args){
02858         HELP("PROMPT");
02859         if(args && *args) {
02860                 args++;
02861                 SetEnv("PROMPT",args);
02862         } else
02863                 SetEnv("PROMPT","$P$G");
02864         return;
02865 }
02866 
02867 void DOS_Shell::CMD_PATH(char *args){
02868         HELP("PATH");
02869         if(args && *args){
02870                 char pathstring[DOS_PATHLENGTH+CROSS_LEN+20]={ 0 };
02871                 strcpy(pathstring,"set PATH=");
02872                 while(args && (*args=='='|| *args==' ')) 
02873                      args++;
02874         if (args)
02875             strcat(pathstring,args);
02876                 this->ParseLine(pathstring);
02877                 return;
02878         } else {
02879                 std::string line;
02880                 if(GetEnvStr("PATH",line)) {
02881                         WriteOut("%s\n",line.c_str());
02882                 } else {
02883                         WriteOut("PATH=(null)\n");
02884                 }
02885         }
02886 }
02887 
02888 void DOS_Shell::CMD_VERIFY(char * args) {
02889         HELP("VERIFY");
02890         args = trim(args);
02891         if (!*args)
02892                 WriteOut("VERIFY is %s\n", dos.verify ? "on" : "off");
02893         else if (!strcasecmp(args, "OFF"))
02894                 dos.verify = false;
02895         else if (!strcasecmp(args, "ON"))
02896                 dos.verify = true;
02897         else
02898                 WriteOut("Must specify ON or OFF\n");
02899 }
02900 
02901 void DOS_Shell::CMD_VER(char *args) {
02902         HELP("VER");
02903         bool optR=ScanCMDBool(args,"R");
02904         if (char* rem = ScanCMDRemain(args)) {
02905                 WriteOut("Invalid switch - %s\n", rem);
02906                 return;
02907         }
02908         if(!optR && args && *args) {
02909                 char* word = StripWord(args);
02910                 if(strcasecmp(word,"set")) return;
02911                 word = StripWord(args);
02912                 if (!*args && !*word) { //Reset
02913                         dos.version.major = 5;
02914                         dos.version.minor = 0;
02915                 } else if (*args == 0 && *word && (strchr(word,'.') != 0)) { //Allow: ver set 5.1
02916                         const char * p = strchr(word,'.');
02917                         dos.version.major = (Bit8u)(atoi(word));
02918                         dos.version.minor = (Bit8u)(strlen(p+1)==1&&*(p+1)>'0'&&*(p+1)<='9'?atoi(p+1)*10:atoi(p+1));
02919                 } else { //Official syntax: ver set 5 2
02920                         dos.version.major = (Bit8u)(atoi(word));
02921                         dos.version.minor = (Bit8u)(atoi(args));
02922                 }
02923                 if (enablelfn != -2) uselfn = enablelfn==1 || (enablelfn == -1 && dos.version.major>6);
02924         } else {
02925                 WriteOut(MSG_Get("SHELL_CMD_VER_VER"),VERSION,SDL_STRING,dos.version.major,dos.version.minor);
02926                 if (optR) WriteOut("DOSBox-X's build date and time: %s\n",UPDATED_STR);
02927         }
02928 }
02929 
02930 void DOS_Shell::CMD_VOL(char *args){
02931         HELP("VOL");
02932         Bit8u drive=DOS_GetDefaultDrive();
02933         if(args && *args){
02934                 args++;
02935                 Bit32u argLen = (Bit32u)strlen(args);
02936                 switch (args[argLen-1]) {
02937                 case ':' :
02938                         if(!strcasecmp(args,":")) return;
02939                         int drive2; drive2= toupper(*reinterpret_cast<unsigned char*>(&args[0]));
02940                         char * c; c = strchr(args,':'); *c = '\0';
02941                         if (Drives[drive2-'A']) drive = drive2 - 'A';
02942                         else {
02943                                 WriteOut(MSG_Get("SHELL_CMD_VOL_DRIVEERROR"));
02944                                 return;
02945                         }
02946                         break;
02947                 default:
02948                         WriteOut(MSG_Get("SHELL_SYNTAXERROR"));
02949                         return;
02950                 }
02951         }
02952         char const* bufin = Drives[drive]->GetLabel();
02953         WriteOut(MSG_Get("SHELL_CMD_VOL_DRIVE"),drive+'A');
02954 
02955         //if((drive+'A')=='Z') bufin="DOSBOX-X";
02956         if(strcasecmp(bufin,"")==0)
02957                 WriteOut(MSG_Get("SHELL_CMD_VOL_SERIAL_NOLABEL"));
02958         else
02959                 WriteOut(MSG_Get("SHELL_CMD_VOL_SERIAL_LABEL"),bufin);
02960 
02961         WriteOut(MSG_Get("SHELL_CMD_VOL_SERIAL"));
02962         unsigned long serial_number=0x1234;
02963         if (!strncmp(Drives[drive]->GetInfo(),"fatDrive ",9)) {
02964                 fatDrive* fdp = dynamic_cast<fatDrive*>(Drives[drive]);
02965                 if (fdp != NULL) serial_number=fdp->GetSerial();
02966         }
02967 #if defined (WIN32)
02968         if (!strncmp(Drives[drive]->GetInfo(),"local ",6) || !strncmp(Drives[drive]->GetInfo(),"CDRom ",6)) {
02969                 localDrive* ldp = !strncmp(Drives[drive]->GetInfo(),"local ",6)?dynamic_cast<localDrive*>(Drives[drive]):dynamic_cast<cdromDrive*>(Drives[drive]);
02970                 if (ldp != NULL) serial_number=ldp->GetSerial();
02971         }
02972 #endif
02973         WriteOut("%04X-%04X\n", serial_number/0x10000, serial_number%0x10000);
02974         return;
02975 }
02976 
02977 void DOS_Shell::CMD_TRUENAME(char * args) {
02978         HELP("TRUENAME");
02979         args = trim(args);
02980         if (!*args) {
02981                 WriteOut("No file name given.\n");
02982                 return;
02983         }
02984         if (char* rem = ScanCMDRemain(args)) {
02985                 WriteOut("Invalid switch - %s\n", rem);
02986                 return;
02987         }
02988         char *name = StripArg(args), fullname[DOS_PATHLENGTH];
02989         Bit8u drive;
02990         if (DOS_MakeName(name, fullname, &drive))
02991                 WriteOut("%c:\\%s\r\n", drive+'A', fullname);
02992         else
02993                 WriteOut(dos.errorcode==DOSERR_PATH_NOT_FOUND?"Path not found\n":"File not found\n");
02994 }
02995 
02996 void SetVal(const std::string& secname, const std::string& preval, const std::string& val);
02997 static void delayed_press(Bitu key) { KEYBOARD_AddKey((KBD_KEYS)key,true); }
02998 static void delayed_release(Bitu key) { KEYBOARD_AddKey((KBD_KEYS)key,false); }
02999 static void delayed_sdlpress(Bitu core) {
03000         if(core==1) SetVal("cpu","core","normal");
03001         else if(core==2) SetVal("cpu","core","simple");
03002         else if(core==3) SetVal("cpu","core","dynamic");
03003         else if(core==4) SetVal("cpu","core","full");
03004 }
03005 // ADDKEY patch was created by Moe
03006 void DOS_Shell::CMD_ADDKEY(char * args){
03007         HELP("ADDKEY");
03008         StripSpaces(args);
03009         if (!*args) {
03010                 WriteOut(MSG_Get("SHELL_SYNTAXERROR"));
03011                 return;
03012         }
03013     pic_tickindex_t delay = 0;
03014     int duration = 0, core = 0;
03015 
03016         while (*args) {
03017                 char *word=StripWord(args);
03018                 KBD_KEYS scankey = (KBD_KEYS)0;
03019                 char *tail;
03020                 bool alt = false, ctrl = false, shift = false;
03021                 while (word[1] == '-') {
03022                         switch (word[0]) {
03023                                 case 'c':
03024                                         ctrl = true;
03025                                         word += 2;
03026                                         break;
03027                                 case 's':
03028                                         shift = true;
03029                                         word += 2;
03030                                         break;
03031                                 case 'a':
03032                                         alt = true;
03033                                         word += 2;
03034                                         break;
03035                                 default:
03036                                         WriteOut(MSG_Get("SHELL_SYNTAXERROR"));
03037                                         return;
03038                         }
03039                 }
03040                 if (!strcasecmp(word,"enter")) {
03041                         word[0] = (char)10;
03042                         word[1] = (char)0;
03043                 } else if (!strcasecmp(word,"space")) {
03044                         word[0] = (char)32;
03045                         word[1] = (char)0;
03046                 } else if (!strcasecmp(word,"bs")) {
03047                         word[0] = (char)8;
03048                         word[1] = (char)0;
03049                 } else if (!strcasecmp(word,"tab")) {
03050                         word[0] = (char)9;
03051                         word[1] = (char)0;
03052                 } else if (!strcasecmp(word,"escape")) {
03053                         word[0] = (char)27;
03054                         word[1] = (char)0;
03055                 } else if (!strcasecmp(word,"up")) {
03056                         word[0] = (char)141;
03057                         word[1] = (char)0;
03058                 } else if (!strcasecmp(word,"down")) {
03059                         word[0] = (char)142;
03060                         word[1] = (char)0;
03061                 } else if (!strcasecmp(word,"left")) {
03062                         word[0] = (char)143;
03063                         word[1] = (char)0;
03064                 } else if (!strcasecmp(word,"right")) {
03065                         word[0] = (char)144;
03066                         word[1] = (char)0;
03067                 } else if (!strcasecmp(word,"ins")) {
03068                         word[0] = (char)145;
03069                         word[1] = (char)0;
03070                 } else if (!strcasecmp(word,"del")) {
03071                         word[0] = (char)146;
03072                         word[1] = (char)0;
03073                 } else if (!strcasecmp(word,"home")) {
03074                         word[0] = (char)147;
03075                         word[1] = (char)0;
03076                 } else if (!strcasecmp(word,"end")) {
03077                         word[0] = (char)148;
03078                         word[1] = (char)0;
03079                 } else if (!strcasecmp(word,"pgup")) {
03080                         word[0] = (char)149;
03081                         word[1] = (char)0;
03082                 } else if (!strcasecmp(word,"pgdown")) {
03083                         word[0] = (char)150;
03084                         word[1] = (char)0;
03085                 } else if (!strcasecmp(word,"normal")) {
03086                         core = 1;
03087                 } else if (!strcasecmp(word,"simple")) {
03088                         core = 2;
03089                 } else if (!strcasecmp(word,"dynamic")) {
03090                         core = 3;
03091                 } else if (!strcasecmp(word,"full")) {
03092                         core = 4;
03093                 } else if (word[0] == 'k' && word[1] == 'p' && word[2] && !word[3]) {
03094                         word[0] = 151+word[2]-'0';
03095                         word[1] = 0;
03096                 } else if (word[0] == 'f' && word[1]) {
03097                         word[0] = 128+word[1]-'0';
03098                         if (word[1] == '1' && word[2]) word[0] = 128+word[2]-'0'+10;
03099                         word[1] = 0;
03100                 }
03101                 if (!word[1]) {
03102                         const int shiftflag = 0x1000000;
03103                         const int map[256] = {
03104                                 0,0,0,0,0,0,0,0,
03105                                 KBD_backspace,
03106                                 KBD_tab,
03107                                 KBD_enter,
03108                                 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
03109                                 KBD_esc,
03110                                 0,0,0,0,
03111                                 KBD_space, KBD_1|shiftflag, KBD_quote|shiftflag, KBD_3|shiftflag, KBD_4|shiftflag, KBD_5|shiftflag, KBD_7|shiftflag, KBD_quote,
03112                                 KBD_9|shiftflag, KBD_0|shiftflag, KBD_8|shiftflag, KBD_equals|shiftflag, KBD_comma, KBD_minus, KBD_period, KBD_slash,
03113                                 KBD_0, KBD_1, KBD_2, KBD_3, KBD_4, KBD_5, KBD_6, KBD_7,
03114                                 KBD_8, KBD_9, KBD_semicolon|shiftflag, KBD_semicolon, KBD_comma|shiftflag, KBD_equals, KBD_period|shiftflag, KBD_slash|shiftflag,
03115                                 KBD_2|shiftflag, KBD_a|shiftflag, KBD_b|shiftflag, KBD_c|shiftflag, KBD_d|shiftflag, KBD_e|shiftflag, KBD_f|shiftflag, KBD_g|shiftflag,
03116                                 KBD_h|shiftflag, KBD_i|shiftflag, KBD_j|shiftflag, KBD_k|shiftflag, KBD_l|shiftflag, KBD_m|shiftflag, KBD_n|shiftflag, KBD_o|shiftflag,
03117                                 KBD_p|shiftflag, KBD_q|shiftflag, KBD_r|shiftflag, KBD_s|shiftflag, KBD_t|shiftflag, KBD_u|shiftflag, KBD_v|shiftflag, KBD_w|shiftflag,
03118                                 KBD_x|shiftflag, KBD_y|shiftflag, KBD_z|shiftflag, KBD_leftbracket, KBD_backslash, KBD_rightbracket, KBD_6|shiftflag, KBD_minus|shiftflag,
03119                                 KBD_grave, KBD_a, KBD_b, KBD_c, KBD_d, KBD_e, KBD_f, KBD_g,
03120                                 KBD_h, KBD_i, KBD_j, KBD_k, KBD_l, KBD_m, KBD_n, KBD_o,
03121                                 KBD_p, KBD_q, KBD_r, KBD_s, KBD_t, KBD_u, KBD_v, KBD_w,
03122                                 KBD_x, KBD_y, KBD_z, KBD_leftbracket|shiftflag, KBD_backslash|shiftflag, KBD_rightbracket|shiftflag, KBD_grave|shiftflag, 0,
03123                                 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,
03124                                 KBD_up, KBD_down, KBD_left, KBD_right, KBD_insert, KBD_delete, KBD_home, KBD_end, KBD_pageup, KBD_pagedown,
03125                                 KBD_kp0, KBD_kp1, KBD_kp2, KBD_kp3, KBD_kp4, KBD_kp5, KBD_kp6, KBD_kp7, KBD_kp8, KBD_kp9, 
03126                         };
03127                         scankey = (KBD_KEYS)(map[(unsigned char)word[0]] & ~shiftflag);
03128                         if (map[(unsigned char)word[0]] & shiftflag) shift = true;
03129                         if (!scankey && core == 0) {
03130                                 WriteOut(MSG_Get("SHELL_SYNTAXERROR"),word);
03131                                 return;
03132                         }
03133                         if (core == 0) word[0] = 0;
03134                 }
03135                 if (word[0] == 'p') {
03136                         delay += strtol(word+1,&tail,0);
03137                         if (tail && *tail) {
03138                                 WriteOut(MSG_Get("SHELL_SYNTAXERROR"),word);
03139                                 return;
03140                         }
03141                 } else if (word[0] == 'l') {
03142                         duration = strtol(word+1,&tail,0);
03143                         if (tail && *tail) {
03144                                 WriteOut(MSG_Get("SHELL_SYNTAXERROR"),word);
03145                                 return;
03146                         }
03147                 } else if (!word[0] || ((scankey = (KBD_KEYS)strtol(word,NULL,0)) > KBD_NONE && scankey < KBD_LAST)) {
03148                         if (shift) {
03149                                 if (delay == 0) KEYBOARD_AddKey(KBD_leftshift,true);
03150                                 else PIC_AddEvent(&delayed_press,delay++,KBD_leftshift);
03151                         }
03152                         if (ctrl) {
03153                                 if (delay == 0) KEYBOARD_AddKey(KBD_leftctrl,true);
03154                                 else PIC_AddEvent(&delayed_press,delay++,KBD_leftctrl);
03155                         }
03156                         if (alt) {
03157                                 if (delay == 0) KEYBOARD_AddKey(KBD_leftalt,true);
03158                         else PIC_AddEvent(&delayed_press,delay++,KBD_leftalt);
03159                         }
03160                         if (delay == 0) KEYBOARD_AddKey(scankey,true);
03161                         else PIC_AddEvent(&delayed_press,delay++,scankey);
03162 
03163                         if (delay+duration == 0) KEYBOARD_AddKey(scankey,false);
03164                         else PIC_AddEvent(&delayed_release,delay+++duration,scankey);
03165                         if (alt) {
03166                                 if (delay+duration == 0) KEYBOARD_AddKey(KBD_leftalt,false);
03167                                 else PIC_AddEvent(&delayed_release,delay+++duration,KBD_leftalt);
03168                         }
03169                         if (ctrl) {
03170                                 if (delay+duration == 0) KEYBOARD_AddKey(KBD_leftctrl,false);
03171                                 else PIC_AddEvent(&delayed_release,delay+++duration,KBD_leftctrl);
03172                         }
03173                         if (shift) {
03174                                 if (delay+duration == 0) KEYBOARD_AddKey(KBD_leftshift,false);
03175                                 else PIC_AddEvent(&delayed_release,delay+++duration,KBD_leftshift);
03176                         }
03177                 } else if (core != 0) {
03178                         if (core == 1) {
03179                                 if (delay == 0) SetVal("cpu","core","normal");
03180                                 else PIC_AddEvent(&delayed_sdlpress,delay++,1);
03181                         } else if (core == 2) {
03182                                 if (delay == 0) SetVal("cpu","core","simple");
03183                                 else PIC_AddEvent(&delayed_sdlpress,delay++,2);
03184                         } else if (core == 3) {
03185                                 if (delay == 0) SetVal("cpu","core","dynamic");
03186                                 else PIC_AddEvent(&delayed_sdlpress,delay++,3);
03187                         } else if (core == 4) {
03188                                 if (delay == 0) SetVal("cpu","core","full");
03189                                 else PIC_AddEvent(&delayed_sdlpress,delay++,4);
03190                         }
03191                 } else {
03192                         WriteOut(MSG_Get("SHELL_SYNTAXERROR"),word);
03193                         return;
03194                 }
03195         }
03196  }
03197 
03198 #if C_DEBUG
03199 bool debugger_break_on_exec = false;
03200 
03201 void DOS_Shell::CMD_DEBUGBOX(char * args) {
03202     while (*args == ' ') args++;
03203         std::string argv=std::string(args);
03204         args=StripArg(args);
03205         HELP("DEBUGBOX");
03206     /* TODO: The command as originally taken from DOSBox SVN supported a /NOMOUSE option to remove the INT 33h vector */
03207     debugger_break_on_exec = true;
03208     if (!strcmp(args,"-?")) {
03209                 args[0]='/';
03210                 HELP("DEBUGBOX");
03211                 return;
03212         }
03213     DoCommand((char *)argv.c_str());
03214     debugger_break_on_exec = false;
03215 }
03216 #endif
03217 
03218 static char *str_replace(char *orig, char *rep, char *with) {
03219     char *result, *ins, *tmp;
03220     size_t len_rep, len_with, len_front;
03221     int count;
03222 
03223     if (!orig || !rep) return NULL;
03224     len_rep = strlen(rep);
03225     if (len_rep == 0) return NULL;
03226     len_with = with?strlen(with):0;
03227 
03228     ins = orig;
03229     for (count = 0; (tmp = strstr(ins, rep)) != NULL; ++count)
03230         ins = tmp + len_rep;
03231 
03232     tmp = result = (char *)malloc(strlen(orig) + (len_with - len_rep) * count + 1);
03233 
03234     if (!result) return NULL;
03235     while (count--) {
03236         ins = strstr(orig, rep);
03237         len_front = ins - orig;
03238         tmp = strncpy(tmp, orig, len_front) + len_front;
03239         tmp = strcpy(tmp, with?with:"") + len_with;
03240         orig += len_front + len_rep;
03241     }
03242     strcpy(tmp, orig);
03243     return result;
03244 }
03245 
03246 void DOS_Shell::CMD_FOR(char *args) {
03247         HELP("FOR");
03248         args = ltrim(args);
03249         if (strlen(args)<12){SyntaxError();return;}
03250         char s[3];
03251         strcpy(s,"%%");
03252         if (*args=='%' && (isalpha(args[1]) || isdigit(args[1]) || strchr("_-/*.;#$",args[1])) && isspace(args[2]))
03253                 s[1]=*(args+1);
03254         else{SyntaxError();return;}
03255         args = ltrim(args+3);
03256         if (strncasecmp(args, "IN", 2) || !isspace(args[2])){SyntaxError();return;}
03257         args = ltrim(args+3);
03258         if (*args=='(')
03259                 args = ltrim(args+1);
03260         else{SyntaxError();return;}
03261         char *p=strchr(args, ')');
03262         if (p==NULL||!isspace(*(p+1))){SyntaxError();return;}
03263         *p=0;
03264         char flist[260], *fp=flist;
03265         if (strlen(ltrim(args))<260)
03266                 strcpy(flist, ltrim(args));
03267         else
03268                 {
03269                 strncpy(flist, args, 259);
03270                 flist[259]=0;
03271                 }
03272         *p=')';
03273         args=ltrim(p+2);
03274         if (strncasecmp(args, "DO", 2) || !isspace(args[2])){SyntaxError();return;}
03275         args = ltrim(args+3);
03276         bool lfn=uselfn&&lfnfor;
03277         while (*fp) {
03278                 p=fp;
03279                 int q=0;
03280                 while (*p&&(q/2*2!=q||(*p!=' '&&*p!=','&&*p!=';')))
03281                         {
03282                         if (*p=='"')
03283                                 q++;
03284                         p++;
03285                         }
03286                 bool last=!!strlen(p);
03287                 if (last) *p=0;
03288                 if (strchr(fp, '?') || strchr(fp, '*')) {
03289                         char name[DOS_NAMELENGTH_ASCII], lname[LFN_NAMELENGTH], spath[DOS_PATHLENGTH], path[DOS_PATHLENGTH], pattern[DOS_PATHLENGTH], full[DOS_PATHLENGTH], *r;
03290                         if (!DOS_Canonicalize(fp,full)) return;
03291                         r=strrchr(full, '\\');
03292                         if (r!=NULL) {
03293                                 *r=0;
03294                                 strcpy(path, full);
03295                                 strcat(path, "\\");
03296                                 strcpy(pattern, r+1);
03297                                 *r='\\';
03298                         } else {
03299                                 strcpy(path, "");
03300                                 strcpy(pattern, full);
03301                         }
03302                         strcpy(spath, path);
03303                         if (strchr(fp,'\"')||uselfn) {
03304                                 if (!DOS_GetSFNPath(("\""+std::string(path)+"\\").c_str(), spath, false)) strcpy(spath, path);
03305                                 if (!strlen(spath)||spath[strlen(spath)-1]!='\\') strcat(spath, "\\");
03306                                 int k=0;
03307                                 for (int i=0;i<(int)strlen(path);i++)
03308                                         if (path[i]!='\"')
03309                                                 path[k++]=path[i];
03310                                 path[k]=0;
03311                         }
03312                         Bit32u size;
03313                         Bit16u date, time;
03314                         Bit8u attr;
03315                         DOS_DTA dta(dos.dta());
03316                         std::vector<std::string> sources;
03317                         std::string tmp;
03318                         int fbak=lfn_filefind_handle;
03319                         lfn_filefind_handle=lfn?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE;
03320                         if (DOS_FindFirst((char *)(std::string(spath)+std::string(pattern)).c_str(), ~(DOS_ATTR_VOLUME|DOS_ATTR_DIRECTORY|DOS_ATTR_DEVICE|DOS_ATTR_HIDDEN|DOS_ATTR_SYSTEM)))
03321                                 {
03322                                 dta.GetResult(name, lname, size, date, time, attr);
03323                                 tmp=std::string(path)+std::string(lfn?lname:name);
03324                                 sources.push_back(tmp);
03325                                 while (DOS_FindNext())
03326                                         {
03327                                         dta.GetResult(name, lname, size, date, time, attr);
03328                                         tmp=std::string(path)+std::string(lfn?lname:name);
03329                                         sources.push_back(tmp);
03330                                         }
03331                                 }
03332                         lfn_filefind_handle=fbak;
03333                         for (std::vector<std::string>::iterator source = sources.begin(); source != sources.end(); ++source)
03334                                 DoCommand(str_replace(args, s, (char *)source->c_str()));
03335                 } else
03336                         DoCommand(str_replace(args, s, fp));
03337                 if (last) *p=' ';
03338                 fp=ltrim(p);
03339         }
03340 }
03341 
03342 void DOS_Shell::CMD_LFNFOR(char * args) {
03343         HELP("LFNFOR");
03344         args = trim(args);
03345         if (!*args)
03346                 WriteOut("LFNFOR is %s\n", lfnfor ? "on" : "off");
03347         else if (!strcasecmp(args, "OFF"))
03348                 lfnfor = false;
03349         else if (!strcasecmp(args, "ON"))
03350                 lfnfor = true;
03351         else
03352                 WriteOut("Must specify ON or OFF\n");
03353 }
03354 
03355 void DOS_Shell::CMD_ALIAS(char* args) {
03356     HELP("ALIAS");
03357         args = trim(args);
03358     if (!*args || strchr(args, '=') == NULL) {
03359         for (cmd_alias_map_t::iterator iter = cmd_alias.begin(), end = cmd_alias.end();
03360             iter != end; ++iter) {
03361                         if (!*args || !strcasecmp(args, iter->first.c_str()))
03362                                 WriteOut("ALIAS %s='%s'\n", iter->first.c_str(), iter->second.c_str());
03363         }
03364     } else {
03365         char alias_name[256] = { 0 };
03366         char* cmd = 0;
03367         for (unsigned int offset = 0; *args && offset < sizeof(alias_name)-1; ++offset, ++args) {
03368             if (*args == '=') {
03369                 cmd = trim(alias_name);
03370                 ++args;
03371                 args = trim(args);
03372                 size_t args_len = strlen(args);
03373                 if ((*args == '"' && args[args_len - 1] == '"') || (*args == '\'' && args[args_len - 1] == '\'')) {
03374                     args[args_len - 1] = 0;
03375                     ++args;
03376                 }
03377                 if (!*args) {
03378                     cmd_alias.erase(cmd);
03379                 } else {
03380                     cmd_alias[cmd] = args;
03381                 }
03382                 break;
03383             } else {
03384                 alias_name[offset] = *args;
03385             }
03386         }
03387     }
03388 }
03389 
03390 void CAPTURE_StartCapture(void);
03391 void CAPTURE_StopCapture(void);
03392 
03393 void CAPTURE_StartWave(void);
03394 void CAPTURE_StopWave(void);
03395 
03396 void CAPTURE_StartMTWave(void);
03397 void CAPTURE_StopMTWave(void);
03398 
03399 // Explanation: Start capture, run program, stop capture when program exits.
03400 //              Great for gameplay footage or demoscene capture.
03401 //
03402 //              The command name is chosen not to conform to the 8.3 pattern
03403 //              on purpose to avoid conflicts with any existing DOS applications.
03404 void DOS_Shell::CMD_DXCAPTURE(char * args) {
03405     while (*args == ' ') args++;
03406         std::string argv=std::string(args);
03407         args=StripArg(args);
03408         HELP("DXCAPTURE");
03409     bool cap_video = false;
03410     bool cap_audio = false;
03411     bool cap_mtaudio = false;
03412     unsigned long post_exit_delay_ms = 3000; /* 3 sec */
03413 
03414     if (!strcmp(args,"-?")) {
03415                 args[0]='/';
03416                 HELP("DXCAPTURE");
03417                 return;
03418         }
03419 
03420     args=(char *)argv.c_str();
03421     char *arg1;
03422     while (strlen(args)&&args[0]=='/') {
03423                 arg1=StripArg(args);
03424                 upcase(arg1);
03425                 if (!(strcmp(arg1,"/V")))
03426                         cap_video = true;
03427                 else if (!(strcmp(arg1,"/-V")))
03428                         cap_video = false;
03429                 else if (!(strcmp(arg1,"/A")))
03430                         cap_audio = true;
03431                 else if (!(strcmp(arg1,"/-A")))
03432                         cap_audio = false;
03433                 else if (!(strcmp(arg1,"/M")))
03434                         cap_mtaudio = true;
03435                 else if (!(strcmp(arg1,"/-M")))
03436                         cap_mtaudio = false;
03437                 else {
03438                         WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),arg1);
03439                         return;
03440                 }
03441     }
03442 
03443     if (!cap_video && !cap_audio && !cap_mtaudio)
03444         cap_video = true;
03445 
03446     if (cap_video)
03447         CAPTURE_StartCapture();
03448     if (cap_audio)
03449         CAPTURE_StartWave();
03450     if (cap_mtaudio)
03451         CAPTURE_StartMTWave();
03452 
03453     DoCommand(args);
03454 
03455     if (post_exit_delay_ms > 0) {
03456         LOG_MSG("Pausing for post exit delay (%.3f seconds)",(double)post_exit_delay_ms / 1000);
03457 
03458         Bit32u lasttick=GetTicks();
03459         while ((GetTicks()-lasttick)<post_exit_delay_ms) {
03460             CALLBACK_Idle();
03461 
03462             if (machine == MCH_PC98) {
03463                 reg_eax = 0x0100;   // sense key
03464                 CALLBACK_RunRealInt(0x18);
03465                 SETFLAGBIT(ZF,reg_bh == 0);
03466             }
03467             else {
03468                 reg_eax = 0x0100;
03469                 CALLBACK_RunRealInt(0x16);
03470             }
03471 
03472             if (!GETFLAG(ZF)) {
03473                 if (machine == MCH_PC98) {
03474                     reg_eax = 0x0000;   // read key
03475                     CALLBACK_RunRealInt(0x18);
03476                 }
03477                 else {
03478                     reg_eax = 0x0000;
03479                     CALLBACK_RunRealInt(0x16);
03480                 }
03481 
03482                 if (reg_al == 32/*space*/ || reg_al == 27/*escape*/)
03483                     break;
03484             }
03485         }
03486     }
03487 
03488     if (cap_video)
03489         CAPTURE_StopCapture();
03490     if (cap_audio)
03491         CAPTURE_StopWave();
03492     if (cap_mtaudio)
03493         CAPTURE_StopMTWave();
03494 }
03495 
03496 void DOS_Shell::CMD_CTTY(char * args) {
03497         HELP("CTTY");
03498         /* NTS: This is written to emulate the simplistic parsing in MS-DOS 6.22 */
03499         Bit16u handle;
03500         int i;
03501 
03502         /* args has leading space? */
03503         args = trim(args);
03504 
03505         /* must be device */
03506         if (DOS_FindDevice(args) == DOS_DEVICES) {
03507                 WriteOut("Invalid device");
03508                 return;
03509         }
03510 
03511         /* close STDIN/STDOUT/STDERR and replace with new handle */
03512         if (!DOS_OpenFile(args,OPEN_READWRITE,&handle)) {
03513                 WriteOut("Unable to open device");
03514                 return;
03515         }
03516 
03517         for (i=0;i < 3;i++) {
03518                 DOS_CloseFile(i);
03519                 DOS_ForceDuplicateEntry(handle,i);
03520         }
03521         DOS_CloseFile(handle);
03522 }
03523 
03524 void DOS_Shell::CMD_COUNTRY(char * args) {
03525         HELP("COUNTRY");
03526         if (char* rem = ScanCMDRemain(args))
03527                 {
03528                 WriteOut("Invalid switch - %s\r\n", rem);
03529                 return;
03530                 }
03531         args = trim(args);
03532         if (!*args)
03533                 {
03534                 WriteOut("Current country code: %d\r\n", countryNo);
03535                 return;
03536                 }
03537         int newCC;
03538         char buffer[256];
03539         if (sscanf(args, "%d%s", &newCC, buffer) == 1 && newCC>0)
03540                 {
03541                 countryNo = newCC;
03542                 DOS_SetCountry(countryNo);
03543                 return;
03544                 }
03545         WriteOut("Invalid country code\r\n");
03546         return;
03547 }
03548