DOSBox-X
|
00001 /* 00002 * Copyright (C) 2002-2020 The DOSBox Team 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License along 00015 * with this program; if not, write to the Free Software Foundation, Inc., 00016 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 * 00018 * 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("[2J"); 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,§ors_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,§ors_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,§ors_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