mirror of
https://github.com/joncampbell123/dosbox-x.git
synced 2025-05-09 20:01:19 +08:00
4680 lines
162 KiB
C++
4680 lines
162 KiB
C++
/*
|
|
* Copyright (C) 2002-2021 The DOSBox Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Heavy improvements by the DOSBox-X Team, 2011-2021
|
|
* DX-CAPTURE, DEBUGBOX, INT2FDBG commands by joncampbell123
|
|
* ATTRIB, CHCP, COUNTRY, DELTREE, FOR/LFNFOR, POPD/PUSHD, TREE, TRUENAME, VERIFY commands by Wengier
|
|
* LS command by the DOSBox Staging Team and Wengier
|
|
*/
|
|
|
|
|
|
#include "dosbox.h"
|
|
#include "logging.h"
|
|
#include "shell.h"
|
|
#include "callback.h"
|
|
#include "dos_inc.h"
|
|
#include "regs.h"
|
|
#include "pic.h"
|
|
#include "keyboard.h"
|
|
#include "timer.h"
|
|
#include "../ints/int10.h"
|
|
#include <time.h>
|
|
#include <assert.h>
|
|
#include "bios.h"
|
|
#include "../dos/drives.h"
|
|
#include "support.h"
|
|
#include "control.h"
|
|
#include "paging.h"
|
|
#include "menu.h"
|
|
#include "jfont.h"
|
|
#include "render.h"
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include <cctype>
|
|
#include <cstdlib>
|
|
#include <vector>
|
|
#include <string>
|
|
#include "sdlmain.h"
|
|
#include "menudef.h"
|
|
#include "build_timestamp.h"
|
|
#include "version_string.h"
|
|
|
|
#include <output/output_ttf.h>
|
|
|
|
#if defined(_MSC_VER)
|
|
# pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */
|
|
#endif
|
|
|
|
SHELL_Cmd cmd_list[]={
|
|
{ "DIR", 0, &DOS_Shell::CMD_DIR, "SHELL_CMD_DIR_HELP"},
|
|
{ "CD", 0, &DOS_Shell::CMD_CHDIR, "SHELL_CMD_CHDIR_HELP"},
|
|
{ "ALIAS", 1, &DOS_Shell::CMD_ALIAS, "SHELL_CMD_ALIAS_HELP"},
|
|
{ "ASSOC", 1, &DOS_Shell::CMD_ASSOC, "SHELL_CMD_ASSOC_HELP"},
|
|
{ "ATTRIB", 1, &DOS_Shell::CMD_ATTRIB, "SHELL_CMD_ATTRIB_HELP"},
|
|
{ "BREAK", 1, &DOS_Shell::CMD_BREAK, "SHELL_CMD_BREAK_HELP"},
|
|
{ "CALL", 1, &DOS_Shell::CMD_CALL, "SHELL_CMD_CALL_HELP"},
|
|
{ "CHDIR", 1, &DOS_Shell::CMD_CHDIR, "SHELL_CMD_CHDIR_HELP"},
|
|
//{ "CHOICE", 1, &DOS_Shell::CMD_CHOICE, "SHELL_CMD_CHOICE_HELP"}, // CHOICE as a program (Z:\DOS\CHOICE.COM) instead of shell command
|
|
{ "CLS", 0, &DOS_Shell::CMD_CLS, "SHELL_CMD_CLS_HELP"},
|
|
{ "COPY", 0, &DOS_Shell::CMD_COPY, "SHELL_CMD_COPY_HELP"},
|
|
{ "CHCP", 1, &DOS_Shell::CMD_CHCP, "SHELL_CMD_CHCP_HELP"},
|
|
//{ "COUNTRY", 1, &DOS_Shell::CMD_COUNTRY, "SHELL_CMD_COUNTRY_HELP"}, // COUNTRY as a program (Z:\SYSTEM\COUNTRY.COM) instead of shell command
|
|
{ "CTTY", 1, &DOS_Shell::CMD_CTTY, "SHELL_CMD_CTTY_HELP"},
|
|
{ "DATE", 0, &DOS_Shell::CMD_DATE, "SHELL_CMD_DATE_HELP"},
|
|
{ "DEL", 0, &DOS_Shell::CMD_DELETE, "SHELL_CMD_DELETE_HELP"},
|
|
//{ "DELTREE", 1, &DOS_Shell::CMD_DELTREE, "SHELL_CMD_DELTREE_HELP"}, // DELTREE as a program (Z:\DOS\DELTREE.EXE) instead of shell command
|
|
{ "ECHO", 0, &DOS_Shell::CMD_ECHO, "SHELL_CMD_ECHO_HELP"},
|
|
{ "ERASE", 1, &DOS_Shell::CMD_DELETE, "SHELL_CMD_DELETE_HELP"},
|
|
{ "EXIT", 0, &DOS_Shell::CMD_EXIT, "SHELL_CMD_EXIT_HELP"},
|
|
{ "FOR", 1, &DOS_Shell::CMD_FOR, "SHELL_CMD_FOR_HELP"},
|
|
{ "GOTO", 1, &DOS_Shell::CMD_GOTO, "SHELL_CMD_GOTO_HELP"},
|
|
//{ "HELP", 1, &DOS_Shell::CMD_HELP, "SHELL_CMD_HELP_HELP"}, // HELP as a program (Z:\SYSTEM\HELP.COM) instead of shell command
|
|
{ "HISTORY", 1, &DOS_Shell::CMD_HISTORY, "SHELL_CMD_HISTORY_HELP"},
|
|
{ "IF", 1, &DOS_Shell::CMD_IF, "SHELL_CMD_IF_HELP"},
|
|
{ "LFNFOR", 1, &DOS_Shell::CMD_LFNFOR, "SHELL_CMD_LFNFOR_HELP"},
|
|
{ "LH", 1, &DOS_Shell::CMD_LOADHIGH, "SHELL_CMD_LOADHIGH_HELP"},
|
|
{ "LOADHIGH", 1, &DOS_Shell::CMD_LOADHIGH, "SHELL_CMD_LOADHIGH_HELP"},
|
|
//{ "LS", 1, &DOS_Shell::CMD_LS, "SHELL_CMD_LS_HELP"}, // LS as a program (Z:\BIN\LS.COM) instead of shell command
|
|
{ "MD", 0, &DOS_Shell::CMD_MKDIR, "SHELL_CMD_MKDIR_HELP"},
|
|
{ "MKDIR", 1, &DOS_Shell::CMD_MKDIR, "SHELL_CMD_MKDIR_HELP"},
|
|
{ "MORE", 1, &DOS_Shell::CMD_MORE, "SHELL_CMD_MORE_HELP"},
|
|
{ "PATH", 1, &DOS_Shell::CMD_PATH, "SHELL_CMD_PATH_HELP"},
|
|
{ "PAUSE", 1, &DOS_Shell::CMD_PAUSE, "SHELL_CMD_PAUSE_HELP"},
|
|
{ "PROMPT", 0, &DOS_Shell::CMD_PROMPT, "SHELL_CMD_PROMPT_HELP"},
|
|
{ "RD", 0, &DOS_Shell::CMD_RMDIR, "SHELL_CMD_RMDIR_HELP"},
|
|
{ "REM", 1, &DOS_Shell::CMD_REM, "SHELL_CMD_REM_HELP"},
|
|
{ "REN", 0, &DOS_Shell::CMD_RENAME, "SHELL_CMD_RENAME_HELP"},
|
|
{ "RENAME", 1, &DOS_Shell::CMD_RENAME, "SHELL_CMD_RENAME_HELP"},
|
|
{ "RMDIR", 1, &DOS_Shell::CMD_RMDIR, "SHELL_CMD_RMDIR_HELP"},
|
|
{ "SET", 1, &DOS_Shell::CMD_SET, "SHELL_CMD_SET_HELP"},
|
|
{ "SHIFT", 1, &DOS_Shell::CMD_SHIFT, "SHELL_CMD_SHIFT_HELP"},
|
|
{ "SUBST", 1, &DOS_Shell::CMD_SUBST, "SHELL_CMD_SUBST_HELP"},
|
|
{ "TIME", 0, &DOS_Shell::CMD_TIME, "SHELL_CMD_TIME_HELP"},
|
|
{ "TRUENAME", 1, &DOS_Shell::CMD_TRUENAME, "SHELL_CMD_TRUENAME_HELP"},
|
|
{ "TYPE", 0, &DOS_Shell::CMD_TYPE, "SHELL_CMD_TYPE_HELP"},
|
|
{ "VER", 0, &DOS_Shell::CMD_VER, "SHELL_CMD_VER_HELP"},
|
|
{ "VERIFY", 1, &DOS_Shell::CMD_VERIFY, "SHELL_CMD_VERIFY_HELP"},
|
|
{ "VOL", 0, &DOS_Shell::CMD_VOL, "SHELL_CMD_VOL_HELP"},
|
|
{ "POPD", 1, &DOS_Shell::CMD_POPD, "SHELL_CMD_POPD_HELP"},
|
|
{ "PUSHD", 1, &DOS_Shell::CMD_PUSHD, "SHELL_CMD_PUSHD_HELP"},
|
|
#if C_DEBUG
|
|
// Additional commands for debugging purposes in DOSBox-X
|
|
{ "DEBUGBOX", 1, &DOS_Shell::CMD_DEBUGBOX, "SHELL_CMD_DEBUGBOX_HELP"},
|
|
//{ "INT2FDBG", 1, &DOS_Shell::CMD_INT2FDBG, "SHELL_CMD_INT2FDBG_HELP"}, // INT2FDBG as a program (Z:\DEBUG\INT2FDBG.COM) instead of shell command
|
|
#endif
|
|
// Advanced commands specific to DOSBox-X
|
|
//{ "ADDKEY", 1, &DOS_Shell::CMD_ADDKEY, "SHELL_CMD_ADDKEY_HELP"}, // ADDKEY as a program (Z:\BIN\ADDKEY.COM) instead of shell command
|
|
{ "DX-CAPTURE", 1, &DOS_Shell::CMD_DXCAPTURE, "SHELL_CMD_DXCAPTURE_HELP"},
|
|
{0,0,0,0}
|
|
};
|
|
|
|
const char *GetCmdName(int i) {
|
|
size_t n = sizeof(cmd_list)/sizeof(cmd_list[0])-1;
|
|
return i>n?NULL:cmd_list[i].name;
|
|
}
|
|
|
|
extern int enablelfn, lfn_filefind_handle, file_access_tries, lastmsgcp;
|
|
extern bool date_host_forced, usecon, outcon, rsize, autoboxdraw, dbcs_sbcs, sync_time, manualtime, inshell, noassoc, dotype, loadlang;
|
|
extern unsigned long freec;
|
|
extern uint8_t DOS_GetAnsiAttr(void);
|
|
extern uint16_t countryNo, altcp_to_unicode[256];
|
|
extern bool isDBCSCP(), isKanji1(uint8_t chr), shiftjis_lead_byte(int c), TTF_using(void), Network_IsNetworkResource(const char * filename);
|
|
extern bool CheckBoxDrawing(uint8_t c1, uint8_t c2, uint8_t c3, uint8_t c4), GFX_GetPreventFullscreen(void), DOS_SetAnsiAttr(uint8_t attr);
|
|
extern bool systemmessagebox(char const * aTitle, char const * aMessage, char const * aDialogType, char const * aIconType, int aDefaultButton);
|
|
extern void Load_Language(std::string name), SwitchLanguage(int oldcp, int newcp, bool confirm), GetExpandedPath(std::string &path);
|
|
extern void MAPPER_AutoType(std::vector<std::string> &sequence, const uint32_t wait_ms, const uint32_t pace_ms, bool choice);
|
|
extern void DOS_SetCountry(uint16_t countryNo), DOSV_FillScreen(void);
|
|
std::string GetDOSBoxXPath(bool withexe=false);
|
|
FILE *testLoadLangFile(const char *fname);
|
|
|
|
/* support functions */
|
|
static char empty_char = 0;
|
|
static char* empty_string = &empty_char;
|
|
static void StripSpaces(char*&args) {
|
|
while(args && *args && isspace(*reinterpret_cast<unsigned char*>(args)))
|
|
args++;
|
|
}
|
|
|
|
static void StripSpaces(char*&args,char also) {
|
|
while(args && *args && (isspace(*reinterpret_cast<unsigned char*>(args)) || (*args == also)))
|
|
args++;
|
|
}
|
|
|
|
static char* ExpandDot(char*args, char* buffer , size_t bufsize, bool expand) {
|
|
if(*args == '.') {
|
|
if(*(args+1) == 0){
|
|
safe_strncpy(buffer, "*.*", bufsize);
|
|
return buffer;
|
|
}
|
|
if( (*(args+1) != '.') && (*(args+1) != '\\') && expand) {
|
|
buffer[0] = '*';
|
|
buffer[1] = 0;
|
|
if (bufsize > 2) strncat(buffer,args,bufsize - 1 /*used buffer portion*/ - 1 /*trailing zero*/ );
|
|
return buffer;
|
|
} else
|
|
safe_strncpy (buffer, args, bufsize);
|
|
}
|
|
else safe_strncpy(buffer,args, bufsize);
|
|
return buffer;
|
|
}
|
|
|
|
static char* ExpandDotMore(char*args, char* buffer , size_t bufsize) {
|
|
char * find_last;
|
|
find_last=strrchr_dbcs(args,'\\');
|
|
if (find_last!=NULL) find_last++;
|
|
if (find_last!=NULL && *find_last == '.') {
|
|
if(*(find_last+1) == 0){
|
|
safe_strncpy(buffer, args, bufsize);
|
|
return buffer;
|
|
}
|
|
if( (*(find_last+1) != '.')) {
|
|
*find_last = 0;
|
|
strcpy(buffer, args);
|
|
*find_last = '.';
|
|
size_t len = strlen(buffer);
|
|
buffer[len] = '*';
|
|
buffer[len+1] = 0;
|
|
if (bufsize > len + 2) strncat(buffer,find_last,bufsize - len - 1 /*used buffer portion*/ - 1 /*trailing zero*/ );
|
|
else safe_strncpy(buffer, args, bufsize);
|
|
return buffer;
|
|
} else
|
|
safe_strncpy(buffer, args, bufsize);
|
|
}
|
|
else safe_strncpy(buffer, args, bufsize);
|
|
return buffer;
|
|
}
|
|
|
|
bool DOS_Shell::CheckConfig(char* cmd_in,char*line) {
|
|
bool quote=false;
|
|
if (strlen(cmd_in)>2&&cmd_in[0]=='"'&&cmd_in[strlen(cmd_in)-1]=='"') {
|
|
cmd_in[strlen(cmd_in)-1]=0;
|
|
cmd_in++;
|
|
quote=true;
|
|
}
|
|
Section* test = control->GetSectionFromProperty(cmd_in);
|
|
if(!test) return false;
|
|
if(line && !line[0]) {
|
|
std::string val = test->GetPropValue(cmd_in);
|
|
if(val != NO_SUCH_PROPERTY) WriteOut("%s\n",val.c_str());
|
|
return true;
|
|
}
|
|
char newcom[1024]; newcom[0] = 0; strcpy(newcom,"z:\\system\\config -set ");
|
|
if (line != NULL) {
|
|
line=trim(line);
|
|
if (*line=='=') line=trim(++line);
|
|
if (line[0]=='"'&&line[strlen(line)-1]=='"') {
|
|
line[strlen(line)-1]=0;
|
|
line++;
|
|
quote=true;
|
|
}
|
|
if (quote) strcat(newcom,"\"");
|
|
strcat(newcom,test->GetName()); strcat(newcom," ");
|
|
strcat(newcom,cmd_in);
|
|
strcat(newcom, "=");
|
|
strcat(newcom, line);
|
|
if (quote) strcat(newcom,"\"");
|
|
} else
|
|
E_Exit("'line' in CheckConfig is NULL");
|
|
DoCommand(newcom);
|
|
return true;
|
|
}
|
|
|
|
bool enable_config_as_shell_commands = false;
|
|
|
|
bool DOS_Shell::execute_shell_cmd(char *name, char *arguments) {
|
|
// SHELL_Cmd shell_cmd = {}; /* unused */
|
|
uint32_t cmd_index=0;
|
|
while (cmd_list[cmd_index].name) {
|
|
if (strcasecmp(cmd_list[cmd_index].name,name)==0) {
|
|
(this->*(cmd_list[cmd_index].handler))(arguments);
|
|
return true;
|
|
}
|
|
cmd_index++;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void DOS_Shell::DoCommand(char * line) {
|
|
/* First split the line into command and arguments */
|
|
std::string origin_cmd_line = line;
|
|
std::string last_alias_cmd;
|
|
std::string altered_cmd_line;
|
|
int alias_counter = 0;
|
|
__do_command_begin:
|
|
if (alias_counter > 64) {
|
|
WriteOut(MSG_Get("SHELL_EXECUTE_ALIAS_EXPAND_OVERFLOW"), origin_cmd_line.c_str());
|
|
}
|
|
line=trim(line);
|
|
char cmd_buffer[CMD_MAXLINE];
|
|
char * cmd_write=cmd_buffer;
|
|
int c=0,q=0;
|
|
while (*line) {
|
|
if (*line == '/' || *line == '\t') break;
|
|
|
|
if ((q & 1) == 0) {
|
|
if (*line == ' ' || *line == '=') break;
|
|
|
|
if (*line == '.' || *line == ';' || (*line == ':' && !(c == 1 && tolower(*(line-1)) >= 'a' && tolower(*(line-1)) <= 'z')) || *line == '[' || *line == ']' || *line == '\\' || *line == '/' || *line == '\"' || *line == '+') { //allow stuff like cd.. and dir.exe cd\kees
|
|
*cmd_write=0;
|
|
if (execute_shell_cmd(cmd_buffer,line)) return;
|
|
}
|
|
}
|
|
c++;
|
|
if (*line == '"') q++;
|
|
*cmd_write++=*line++;
|
|
}
|
|
*cmd_write=0;
|
|
if (strlen(cmd_buffer)==0) {
|
|
if (strlen(line)&&line[0]=='/') WriteOut(MSG_Get("SHELL_EXECUTE_ILLEGAL_COMMAND"),line);
|
|
return;
|
|
}
|
|
cmd_alias_map_t::iterator iter = cmd_alias.find(cmd_buffer);
|
|
if (iter != cmd_alias.end() && last_alias_cmd != cmd_buffer) {
|
|
alias_counter++;
|
|
altered_cmd_line = iter->second + " " + line;
|
|
line = (char*)altered_cmd_line.c_str();
|
|
last_alias_cmd = iter->first;
|
|
goto __do_command_begin;
|
|
}
|
|
|
|
/* Check the internal list */
|
|
if (execute_shell_cmd(cmd_buffer,line)) return;
|
|
|
|
/* This isn't an internal command execute it */
|
|
char ldir[CROSS_LEN], *p=ldir;
|
|
if (strchr(cmd_buffer,'\"')&&DOS_GetSFNPath(cmd_buffer,ldir,false)) {
|
|
if (!strchr_dbcs(cmd_buffer, '\\') && strrchr_dbcs(ldir, '\\'))
|
|
p=strrchr_dbcs(ldir, '\\')+1;
|
|
if (uselfn&&strchr(p, ' ')&&!DOS_FileExists(("\""+std::string(p)+"\"").c_str())) {
|
|
bool append=false;
|
|
if (DOS_FileExists(("\""+std::string(p)+".COM\"").c_str())) {append=true;strcat(p, ".COM");}
|
|
else if (DOS_FileExists(("\""+std::string(p)+".EXE\"").c_str())) {append=true;strcat(p, ".EXE");}
|
|
else if (DOS_FileExists(("\""+std::string(p)+".BAT\"").c_str())) {append=true;strcat(p, ".BAT");}
|
|
if (append&&DOS_GetSFNPath(("\""+std::string(p)+"\"").c_str(), cmd_buffer,false)) if(Execute(cmd_buffer,line)) return;
|
|
}
|
|
if(Execute(p,line)) return;
|
|
} else
|
|
if(Execute(cmd_buffer,line)) return;
|
|
if(enable_config_as_shell_commands && CheckConfig(cmd_buffer,line)) return;
|
|
std::string errhandler = static_cast<Section_prop *>(control->GetSection("dos"))->Get_string("badcommandhandler");
|
|
if (errhandler.size()&&!noassoc) {
|
|
noassoc=true;
|
|
LOG_MSG("errhandler %s line %s\n", errhandler.c_str(), origin_cmd_line.c_str());
|
|
DoCommand((char *)(errhandler+" "+origin_cmd_line).c_str());
|
|
noassoc=false;
|
|
} else
|
|
WriteOut(MSG_Get("SHELL_EXECUTE_ILLEGAL_COMMAND"),cmd_buffer);
|
|
}
|
|
|
|
#define HELP(command) \
|
|
if (ScanCMDBool(args,"?")) { \
|
|
uint8_t attr = DOS_GetAnsiAttr(); \
|
|
WriteOut(MSG_Get("SHELL_CMD_" command "_HELP")); \
|
|
const char* long_m = MSG_Get("SHELL_CMD_" command "_HELP_LONG"); \
|
|
WriteOut("\n"); \
|
|
if(strcmp("Message not Found!\n",long_m)) WriteOut(long_m); \
|
|
else WriteOut(command "\n"); \
|
|
if (attr) DOS_SetAnsiAttr(attr); \
|
|
return; \
|
|
}
|
|
|
|
#if C_DEBUG
|
|
extern Bitu int2fdbg_hook_callback;
|
|
static Bitu INT2FDBG_Handler(void) {
|
|
if (reg_ax == 0x1605) { /* Windows init broadcast */
|
|
int patience = 500;
|
|
Bitu st_seg,st_ofs;
|
|
|
|
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",
|
|
SegValue(es),reg_bx,
|
|
SegValue(ds),reg_si,
|
|
reg_cx,reg_dx,reg_di);
|
|
|
|
st_seg = SegValue(es);
|
|
st_ofs = reg_bx;
|
|
while (st_seg != 0 || st_ofs != 0) {
|
|
unsigned char v_major,v_minor;
|
|
Bitu st_seg_next,st_ofs_next;
|
|
Bitu idrc_seg,idrc_ofs;
|
|
Bitu vdev_seg,vdev_ofs;
|
|
Bitu name_seg,name_ofs;
|
|
char devname[64];
|
|
PhysPt st_o;
|
|
|
|
if (--patience <= 0) {
|
|
LOG_MSG("**WARNING: Chain is too long. Something might have gotten corrupted\n");
|
|
break;
|
|
}
|
|
|
|
st_o = PhysMake(st_seg,st_ofs);
|
|
/* +0x00: Major, minor version of info structure
|
|
* +0x02: pointer to next startup info structure or 0000:0000
|
|
* +0x06: pointer to ASCIIZ name of virtual device or 0000:0000
|
|
* +0x0A: virtual device ref data (pointer to?? or actual data??) or 0000:0000
|
|
* +0x0E: pointer to instance data records or 0000:0000
|
|
* Windows 95 or later (v4.0+):
|
|
* +0x12: pointer to optionally-instanced data records or 0000:0000 */
|
|
v_major = mem_readb(st_o+0x00);
|
|
v_minor = mem_readb(st_o+0x01);
|
|
st_seg_next = mem_readw(st_o+0x02+2);
|
|
st_ofs_next = mem_readw(st_o+0x02+0);
|
|
name_ofs = mem_readw(st_o+0x06+0);
|
|
name_seg = mem_readw(st_o+0x06+2);
|
|
vdev_ofs = mem_readw(st_o+0x0A+0);
|
|
vdev_seg = mem_readw(st_o+0x0A+2);
|
|
idrc_ofs = mem_readw(st_o+0x0A+4); /* FIXME: 0x0E+0 and 0x0E+2 generates weird compiler error WTF?? */
|
|
idrc_seg = mem_readw(st_o+0x0A+6);
|
|
|
|
{
|
|
devname[0] = 0;
|
|
if (name_seg != 0 || name_ofs != 0) {
|
|
unsigned char c;
|
|
unsigned int i;
|
|
PhysPt scan;
|
|
|
|
scan = PhysMake(name_seg,name_ofs);
|
|
for (i=0;i < 63 && (c=mem_readb(scan++)) != 0;) devname[i++] = (char)c;
|
|
devname[i] = 0;
|
|
}
|
|
}
|
|
|
|
LOG_MSG(" >> Version %u.%u\n",v_major,v_minor);
|
|
LOG_MSG(" Next entry at %04x:%04x\n",(int)st_seg_next,(int)st_ofs_next);
|
|
LOG_MSG(" Virtual device name: %04x:%04x '%s'\n",(int)name_seg,(int)name_ofs,devname);
|
|
LOG_MSG(" Virtual dev ref data: %04x:%04x\n",(int)vdev_seg,(int)vdev_ofs);
|
|
LOG_MSG(" Instance data records: %04x:%04x\n",(int)idrc_seg,(int)idrc_ofs);
|
|
|
|
st_seg = st_seg_next;
|
|
st_ofs = st_ofs_next;
|
|
}
|
|
|
|
LOG_MSG("----END CHAIN\n");
|
|
}
|
|
|
|
return CBRET_NONE;
|
|
}
|
|
|
|
void Int2fhook() {
|
|
uint32_t old_int2Fh;
|
|
PhysPt w;
|
|
|
|
int2fdbg_hook_callback = CALLBACK_Allocate();
|
|
CALLBACK_Setup(int2fdbg_hook_callback,&INT2FDBG_Handler,CB_IRET,"INT 2Fh DBG callback");
|
|
|
|
/* record old vector, set our new vector */
|
|
old_int2Fh = RealGetVec(0x2f);
|
|
w = CALLBACK_PhysPointer(int2fdbg_hook_callback);
|
|
RealSetVec(0x2f,CALLBACK_RealPointer(int2fdbg_hook_callback));
|
|
|
|
/* overwrite the callback with code to chain the call down, then invoke our callback on the way back up: */
|
|
|
|
/* first, chain to the previous INT 15h handler */
|
|
phys_writeb(w++,(uint8_t)0x9C); //PUSHF
|
|
phys_writeb(w++,(uint8_t)0x9A); //CALL FAR <address>
|
|
phys_writew(w,(uint16_t)(old_int2Fh&0xFFFF)); w += 2; //offset
|
|
phys_writew(w,(uint16_t)((old_int2Fh>>16)&0xFFFF)); w += 2; //seg
|
|
|
|
/* then, having returned from it, invoke our callback */
|
|
phys_writeb(w++,(uint8_t)0xFE); //GRP 4
|
|
phys_writeb(w++,(uint8_t)0x38); //Extra Callback instruction
|
|
phys_writew(w,(uint16_t)int2fdbg_hook_callback); w += 2; //The immediate word
|
|
|
|
/* return */
|
|
phys_writeb(w++,(uint8_t)0xCF); //IRET
|
|
}
|
|
|
|
/* NTS: I know I could just modify the DOS kernel's INT 2Fh code to receive the init call,
|
|
* the problem is that at that point, the registers do not yet contain anything interesting.
|
|
* all the interesting results of the call are added by TSRs on the way back UP the call
|
|
* chain. The purpose of this program therefore is to hook INT 2Fh on the other end
|
|
* of the call chain so that we can see the results just before returning INT 2Fh back
|
|
* to WIN.COM */
|
|
void DOS_Shell::CMD_INT2FDBG(char * args) {
|
|
//HELP("INT2FDBG");
|
|
while (*args == ' ') args++;
|
|
|
|
/* TODO: Allow /U to remove INT 2Fh hook */
|
|
if (ScanCMDBool(args,"I")) {
|
|
if (int2fdbg_hook_callback == 0) {
|
|
Int2fhook();
|
|
LOG_MSG("INT 2Fh debugging hook set\n");
|
|
WriteOut("INT 2Fh hook has been set.\n");
|
|
} else
|
|
WriteOut("INT 2Fh hook was already set up.\n");
|
|
} else if (*args)
|
|
WriteOut(MSG_Get("SHELL_INVALID_PARAMETER"), args);
|
|
}
|
|
#endif
|
|
|
|
void DOS_Shell::CMD_BREAK(char * args) {
|
|
HELP("BREAK");
|
|
args = trim(args);
|
|
if (!*args)
|
|
WriteOut("BREAK is %s\n", dos.breakcheck ? "on" : "off");
|
|
else if (!strcasecmp(args, "OFF"))
|
|
dos.breakcheck = false;
|
|
else if (!strcasecmp(args, "ON"))
|
|
dos.breakcheck = true;
|
|
else
|
|
WriteOut("Must specify ON or OFF\n");
|
|
}
|
|
|
|
bool is_ANSI_installed(Program *shell);
|
|
void DOS_Shell::CMD_CLS(char * args) {
|
|
HELP("CLS");
|
|
if ((CurMode->type==M_TEXT || IS_PC98_ARCH) && is_ANSI_installed(this))
|
|
WriteOut("\033[2J");
|
|
else {
|
|
uint16_t oldax=reg_ax;
|
|
if (IS_DOSV && DOSV_CheckCJKVideoMode()) reg_ax = GetTrueVideoMode();
|
|
else reg_ax=(uint16_t)CurMode->mode;
|
|
CALLBACK_RunRealInt(0x10);
|
|
reg_ax=oldax;
|
|
}
|
|
}
|
|
|
|
void DOS_Shell::CMD_DELETE(char* args) {
|
|
HELP("DELETE");
|
|
bool optP = ScanCMDBool(args, "P");
|
|
bool optF = ScanCMDBool(args, "F");
|
|
bool optQ = ScanCMDBool(args, "Q");
|
|
|
|
const char ch_y = MSG_Get("INT21_6523_YESNO_CHARS")[0];
|
|
const char ch_n = MSG_Get("INT21_6523_YESNO_CHARS")[1];
|
|
const char ch_Y = toupper(ch_y);
|
|
const char ch_N = toupper(ch_n);
|
|
|
|
// ignore /f, /s, /ar, /as, /ah and /aa switches for compatibility
|
|
ScanCMDBool(args, "S");
|
|
ScanCMDBool(args, "AR");
|
|
ScanCMDBool(args, "AS");
|
|
ScanCMDBool(args, "AH");
|
|
ScanCMDBool(args, "AA");
|
|
|
|
char* rem = ScanCMDRemain(args);
|
|
if(rem) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"), rem);
|
|
return;
|
|
}
|
|
if(!*args) {
|
|
WriteOut(MSG_Get("SHELL_MISSING_PARAMETER"));
|
|
return;
|
|
}
|
|
|
|
StripSpaces(args);
|
|
args = trim(args);
|
|
|
|
/* Command uses dta so set it to our internal dta */
|
|
//DOS_DTA dta(dos.dta());
|
|
RealPt save_dta = dos.dta();
|
|
dos.dta(dos.tables.tempdta);
|
|
DOS_DTA dta(dos.dta());
|
|
/* If delete accept switches mind the space in front of them. See the dir /p code */
|
|
|
|
char full[DOS_PATHLENGTH], sfull[DOS_PATHLENGTH + 2];
|
|
char buffer[CROSS_LEN];
|
|
char name[DOS_NAMELENGTH_ASCII], lname[LFN_NAMELENGTH + 1];
|
|
uint32_t size, hsize; uint16_t time, date; uint8_t attr;
|
|
args = ExpandDot(args, buffer, CROSS_LEN, false);
|
|
StripSpaces(args);
|
|
if(!DOS_Canonicalize(args, full)) { WriteOut(MSG_Get("SHELL_ILLEGAL_PATH")); dos.dta(save_dta); return; }
|
|
if(strlen(args) && args[strlen(args) - 1] != '\\') {
|
|
uint16_t fattr;
|
|
if(strcmp(args, "*.*") && DOS_GetFileAttr(args, &fattr) && (fattr & DOS_ATTR_DIRECTORY))
|
|
strcat(args, "\\");
|
|
}
|
|
if(strlen(args) && args[strlen(args) - 1] == '\\') strcat(args, "*.*");
|
|
else if(!strcmp(args, ".") || (strlen(args) > 1 && (args[strlen(args) - 2] == ':' || args[strlen(args) - 2] == '\\') && args[strlen(args) - 1] == '.')) {
|
|
args[strlen(args) - 1] = '*';
|
|
strcat(args, ".*");
|
|
}
|
|
else if(uselfn && strchr(args, '*')) {
|
|
char* find_last;
|
|
find_last = strrchr_dbcs(args, '\\');
|
|
if(find_last == NULL) find_last = args;
|
|
else find_last++;
|
|
if(strlen(find_last) > 0 && args[strlen(args) - 1] == '*' && strchr(find_last, '.') == NULL) strcat(args, ".*");
|
|
}
|
|
if(!strcmp(args, "*.*") || (strlen(args) > 3 && (!strcmp(args + strlen(args) - 4, "\\*.*") || !strcmp(args + strlen(args) - 4, ":*.*")))) {
|
|
if(!optQ) {
|
|
first_1:
|
|
WriteOut(MSG_Get("SHELL_CMD_DEL_SURE"));
|
|
first_2:
|
|
uint8_t c; uint16_t n = 1;
|
|
DOS_ReadFile(STDIN, &c, &n);
|
|
do {
|
|
if(c == ch_n || c == ch_N) {
|
|
DOS_WriteFile(STDOUT, &c, &n);
|
|
DOS_ReadFile(STDIN, &c, &n);
|
|
do switch(c) {
|
|
case 0xD: WriteOut("\n"); dos.dta(save_dta); return;
|
|
case 0x03: dos.dta(save_dta); return;
|
|
case 0x08: WriteOut("\b \b"); goto first_2;
|
|
} while(DOS_ReadFile(STDIN, &c, &n));
|
|
}
|
|
|
|
if(c == ch_y || c == ch_Y) {
|
|
DOS_WriteFile(STDOUT, &c, &n);
|
|
DOS_ReadFile(STDIN, &c, &n);
|
|
do switch(c) {
|
|
case 0xD: WriteOut("\n"); goto continue_1;
|
|
case 0x03: dos.dta(save_dta); return;
|
|
case 0x08: WriteOut("\b \b"); goto first_2;
|
|
} while(DOS_ReadFile(STDIN, &c, &n));
|
|
}
|
|
|
|
if(c == 0xD) { WriteOut("\n"); goto first_1; }
|
|
if(c == 0x03) { dos.dta(save_dta); return; }
|
|
if(c == '\t' || c == 0x08) goto first_2;
|
|
|
|
DOS_WriteFile(STDOUT, &c, &n);
|
|
DOS_ReadFile(STDIN, &c, &n);
|
|
do switch(c) {
|
|
case 0xD: WriteOut("\n"); goto first_1;
|
|
case 0x03: dos.dta(save_dta); return;
|
|
case 0x08: WriteOut("\b \b"); goto first_2;
|
|
} while(DOS_ReadFile(STDIN, &c, &n));
|
|
goto first_2;
|
|
} while(DOS_ReadFile(STDIN, &c, &n));
|
|
}
|
|
}
|
|
|
|
continue_1:
|
|
/* Command uses dta so set it to our internal dta */
|
|
if(!DOS_Canonicalize(args, full)) { WriteOut(MSG_Get("SHELL_ILLEGAL_PATH")); dos.dta(save_dta); return; }
|
|
char path[DOS_PATHLENGTH], spath[DOS_PATHLENGTH], pattern[DOS_PATHLENGTH], * r = strrchr_dbcs(full, '\\');
|
|
if(r != NULL) {
|
|
*r = 0;
|
|
strcpy(path, full);
|
|
strcat(path, "\\");
|
|
strcpy(pattern, r + 1);
|
|
*r = '\\';
|
|
}
|
|
else {
|
|
strcpy(path, "");
|
|
strcpy(pattern, full);
|
|
}
|
|
int k = 0;
|
|
for(int i = 0; i < (int)strlen(pattern); i++)
|
|
if(pattern[i] != '\"')
|
|
pattern[k++] = pattern[i];
|
|
pattern[k] = 0;
|
|
strcpy(spath, path);
|
|
if(strchr(args, '\"') || uselfn) {
|
|
if(!DOS_GetSFNPath(("\"" + std::string(path) + "\\").c_str(), spath, false)) strcpy(spath, path);
|
|
if(!strlen(spath) || spath[strlen(spath) - 1] != '\\') strcat(spath, "\\");
|
|
}
|
|
std::string pfull = std::string(spath) + std::string(pattern);
|
|
int fbak = lfn_filefind_handle;
|
|
lfn_filefind_handle = uselfn ? LFN_FILEFIND_INTERNAL : LFN_FILEFIND_NONE;
|
|
bool res = DOS_FindFirst(((uselfn && pfull.length() && pfull[0] != '"' ? "\"" : "") + pfull + (uselfn && pfull.length() && pfull[pfull.length() - 1] != '"' ? "\"" : "")).c_str(), 0xffff & ~DOS_ATTR_VOLUME);
|
|
if(!res) {
|
|
lfn_filefind_handle = fbak;
|
|
WriteOut(MSG_Get("SHELL_CMD_DEL_ERROR"), args);
|
|
dos.dta(save_dta);
|
|
return;
|
|
}
|
|
lfn_filefind_handle = fbak;
|
|
//end can't be 0, but if it is we'll get a nice crash, who cares :)
|
|
strcpy(sfull, full);
|
|
char* end = strrchr_dbcs(full, '\\') + 1; *end = 0;
|
|
char* lend = strrchr_dbcs(sfull, '\\') + 1; *lend = 0;
|
|
dta = dos.dta();
|
|
bool exist = false;
|
|
lfn_filefind_handle = uselfn ? LFN_FILEFIND_INTERNAL : LFN_FILEFIND_NONE;
|
|
while(res) {
|
|
dta.GetResult(name, lname, size, hsize, date, time, attr);
|
|
if(!optF && (attr & DOS_ATTR_READ_ONLY) && !(attr & DOS_ATTR_DIRECTORY)) {
|
|
exist = true;
|
|
strcpy(end, name);
|
|
strcpy(lend, lname);
|
|
WriteOut(MSG_Get("SHELL_CMD_DEL_ERROR"), uselfn ? sfull : full);
|
|
}
|
|
else if(!(attr & DOS_ATTR_DIRECTORY)) {
|
|
exist = true;
|
|
strcpy(end, name);
|
|
strcpy(lend, lname);
|
|
if(optP) {
|
|
WriteOut(MSG_Get("SHELL_CMD_DEL_CONFIRM"), uselfn ? sfull : full);
|
|
uint8_t c;
|
|
uint16_t n = 1;
|
|
DOS_ReadFile(STDIN, &c, &n);
|
|
if(c == 3) break;
|
|
c = c == ch_y || c == ch_Y ? ch_Y : ch_N;
|
|
WriteOut("%c\r\n", c);
|
|
if(c == ch_N) { lfn_filefind_handle = uselfn ? LFN_FILEFIND_INTERNAL : LFN_FILEFIND_NONE; res = DOS_FindNext(); continue; }
|
|
}
|
|
if(strlen(full)) {
|
|
std::string pfull = (uselfn || strchr(full, ' ') ? (full[0] != '"' ? "\"" : "") : "") + std::string(full) + (uselfn || strchr(full, ' ') ? (full[strlen(full) - 1] != '"' ? "\"" : "") : "");
|
|
bool reset = false;
|
|
if(optF && (attr & DOS_ATTR_READ_ONLY) && DOS_SetFileAttr(pfull.c_str(), attr & ~DOS_ATTR_READ_ONLY)) reset = true;
|
|
if(!DOS_UnlinkFile(pfull.c_str())) {
|
|
if(optF && reset) DOS_SetFileAttr(pfull.c_str(), attr);
|
|
WriteOut(MSG_Get("SHELL_CMD_DEL_ERROR"), uselfn ? sfull : full);
|
|
}
|
|
}
|
|
else WriteOut(MSG_Get("SHELL_CMD_DEL_ERROR"), uselfn ? sfull : full);
|
|
}
|
|
res = DOS_FindNext();
|
|
}
|
|
lfn_filefind_handle = fbak;
|
|
if(!exist) WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"), args);
|
|
dos.dta(save_dta);
|
|
}
|
|
|
|
size_t GetPauseCount() {
|
|
uint16_t rows;
|
|
if (IS_PC98_ARCH)
|
|
rows=real_readb(0x60,0x113) & 0x01 ? 25 : 20;
|
|
else
|
|
rows=(IS_EGAVGA_ARCH?real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS):24)+1;
|
|
return (rows > 2u) ? (rows - 2u) : 23u;
|
|
}
|
|
|
|
struct DtaResult {
|
|
char name[DOS_NAMELENGTH_ASCII];
|
|
char lname[LFN_NAMELENGTH+1];
|
|
uint32_t size;
|
|
uint32_t hsize;
|
|
uint16_t date;
|
|
uint16_t time;
|
|
uint8_t attr;
|
|
|
|
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); }
|
|
static bool groupExt(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.getExtension(), rhs.getExtension()) < 0); }
|
|
static bool groupSize(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))) && lhs.size+lhs.hsize*0x100000000 < rhs.size+rhs.hsize*0x100000000); }
|
|
static bool groupDate(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))) && (lhs.date < rhs.date || (lhs.date == rhs.date && lhs.time < rhs.time))); }
|
|
static bool groupRevDef(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); }
|
|
static bool groupRevExt(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.getExtension(), rhs.getExtension()) > 0); }
|
|
static bool groupRevSize(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))) && lhs.size+lhs.hsize*0x100000000 > rhs.size+rhs.hsize*0x100000000); }
|
|
static bool groupRevDate(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))) && (lhs.date > rhs.date || (lhs.date == rhs.date && lhs.time > rhs.time))); }
|
|
static bool groupDirs(const DtaResult &lhs, const DtaResult &rhs) { return (lhs.attr & DOS_ATTR_DIRECTORY) && !(rhs.attr & DOS_ATTR_DIRECTORY); }
|
|
static bool compareName(const DtaResult &lhs, const DtaResult &rhs) { return strcmp(lhs.name, rhs.name) < 0; }
|
|
static bool compareExt(const DtaResult &lhs, const DtaResult &rhs) { return strcmp(lhs.getExtension(), rhs.getExtension()) < 0; }
|
|
static bool compareSize(const DtaResult &lhs, const DtaResult &rhs) { return lhs.size+lhs.hsize*0x100000000 < rhs.size+rhs.hsize*0x100000000; }
|
|
static bool compareDate(const DtaResult &lhs, const DtaResult &rhs) { return lhs.date < rhs.date || (lhs.date == rhs.date && lhs.time < rhs.time); }
|
|
|
|
const char * getExtension() const {
|
|
const char * ext = empty_string;
|
|
if (name[0] != '.') {
|
|
ext = strrchr(name, '.');
|
|
if (!ext) ext = empty_string;
|
|
}
|
|
return ext;
|
|
}
|
|
|
|
};
|
|
|
|
extern bool ctrlbrk;
|
|
std::vector<std::string> tdirs;
|
|
|
|
static bool doDeltree(DOS_Shell * shell, char * args, DOS_DTA dta, bool optY, bool first) {
|
|
const char ch_y = MSG_Get("INT21_6523_YESNO_CHARS")[0];
|
|
const char ch_n = MSG_Get("INT21_6523_YESNO_CHARS")[1];
|
|
const char ch_Y = toupper(ch_y);
|
|
const char ch_N = toupper(ch_n);
|
|
char spath[DOS_PATHLENGTH],sargs[DOS_PATHLENGTH+4],path[DOS_PATHLENGTH+4],full[DOS_PATHLENGTH],sfull[DOS_PATHLENGTH+2];
|
|
if (!DOS_Canonicalize(args,full)||strrchr_dbcs(full,'\\')==NULL) { shell->WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));return false; }
|
|
if (!DOS_GetSFNPath(args,spath,false)) {
|
|
if (first) shell->WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args);
|
|
return false;
|
|
}
|
|
if (!uselfn||!DOS_GetSFNPath(args,sfull,true)) strcpy(sfull,full);
|
|
sprintf(sargs,"\"%s\"",spath);
|
|
bool found=false, fdir=false, res=DOS_FindFirst(sargs,0xffff & ~DOS_ATTR_VOLUME);
|
|
if (!res) return false;
|
|
//end can't be 0, but if it is we'll get a nice crash, who cares :)
|
|
uint16_t attribute=0;
|
|
strcpy(path,full);
|
|
if (!first&&strlen(args)>3&&!strcmp(args+strlen(args)-4,"\\.\\.")) {
|
|
if (strlen(path)&&path[strlen(path)-1]=='\\') path[strlen(path)-1]=0;
|
|
if (strlen(path)&&path[strlen(path)-1]!=':') {
|
|
bool reset=false;
|
|
if(DOS_GetFileAttr(path,&attribute) && (attribute&DOS_ATTR_READ_ONLY)&&DOS_SetFileAttr(path, attribute & ~DOS_ATTR_READ_ONLY)) reset=true;
|
|
if (!DOS_RemoveDir(path)&&!(uselfn&&DOS_RemoveDir(sfull))) {
|
|
if (reset) DOS_SetFileAttr(path, attribute);
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_RMDIR_ERROR"),uselfn?sfull:full);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
*(strrchr_dbcs(path,'\\')+1)=0;
|
|
char * end=strrchr_dbcs(full,'\\')+1;*end=0;
|
|
char * lend=strrchr_dbcs(sfull,'\\')+1;*lend=0;
|
|
char name[DOS_NAMELENGTH_ASCII],lname[LFN_NAMELENGTH+1];
|
|
uint32_t size,hsize;uint16_t time,date;uint8_t attr;uint16_t fattr;
|
|
std::vector<std::string> cdirs, cfiles;
|
|
cdirs.clear();
|
|
cfiles.clear();
|
|
std::string pfull;
|
|
while (res) {
|
|
strcpy(spath, path);
|
|
dta.GetResult(name,lname,size,hsize,date,time,attr);
|
|
if (!((!strcmp(name, ".") || !strcmp(name, "..")) && attr & DOS_ATTR_DIRECTORY)) {
|
|
found=true;
|
|
strcpy(end,name);
|
|
strcpy(lend,lname);
|
|
if (strlen(full)&&DOS_GetFileAttr(((uselfn||strchr(full, ' ')?(full[0]!='"'?"\"":""):"")+std::string(full)+(uselfn||strchr(full, ' ')?(full[strlen(full)-1]!='"'?"\"":""):"")).c_str(), &fattr)) {
|
|
uint8_t c;
|
|
uint16_t n=1;
|
|
if(attr&DOS_ATTR_DIRECTORY) {
|
|
if (strcmp(name, ".")&&strcmp(name, "..")) {
|
|
if (!optY&&first) {
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_RMDIR_FULLTREE_CONFIRM"), uselfn ? sfull : full);
|
|
DOS_ReadFile (STDIN,&c,&n);
|
|
if (c==3) {shell->WriteOut("^C\r\n");break;}
|
|
c = c==ch_y||c==ch_Y ? ch_Y:ch_N;
|
|
shell->WriteOut("%c\r\n", c);
|
|
if (c==ch_N) {res = DOS_FindNext();continue;}
|
|
}
|
|
fdir=true;
|
|
strcat(spath, name);
|
|
strcat(spath, "\\*.*");
|
|
cdirs.emplace_back(std::string(spath));
|
|
}
|
|
} else {
|
|
if (!optY&&first) {
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_RMDIR_SINGLE_CONFIRM"), uselfn ? sfull : full);
|
|
DOS_ReadFile (STDIN,&c,&n);
|
|
if (c==3) {shell->WriteOut("^C\r\n");break;}
|
|
c = c==ch_y||c==ch_Y ? ch_Y:ch_N;
|
|
shell->WriteOut("%c\r\n", c);
|
|
if (c==ch_N) {res = DOS_FindNext();continue;}
|
|
}
|
|
pfull=(uselfn||strchr(uselfn?sfull:full, ' ')?((uselfn?sfull:full)[0]!='"'?"\"":""):"")+std::string(uselfn?sfull:full)+(uselfn||strchr(uselfn?sfull:full, ' ')?((uselfn?sfull:full)[strlen(uselfn?sfull:full)-1]!='"'?"\"":""):"");
|
|
cfiles.push_back(pfull);
|
|
}
|
|
}
|
|
}
|
|
res=DOS_FindNext();
|
|
}
|
|
while (!cfiles.empty()) {
|
|
bool reset=false;
|
|
pfull = std::string(cfiles.begin()->c_str());
|
|
if ((attr & DOS_ATTR_READ_ONLY)&&DOS_SetFileAttr(pfull.c_str(), attr & ~DOS_ATTR_READ_ONLY)) reset=true;
|
|
if (!DOS_UnlinkFile(pfull.c_str())) {
|
|
if (reset) DOS_SetFileAttr(pfull.c_str(), attr);
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_DEL_ERROR"),pfull.c_str());
|
|
}
|
|
cfiles.erase(cfiles.begin());
|
|
}
|
|
if (!first&&strlen(args)>4&&!strcmp(args+strlen(args)-4,"\\*.*")) {
|
|
end=strrchr_dbcs(full,'\\')+1;*end=0;
|
|
lend=strrchr_dbcs(sfull,'\\')+1;*lend=0;
|
|
if (fdir) {
|
|
strcpy(spath, path);
|
|
strcat(spath, ".\\.");
|
|
cdirs.push_back(std::string(spath));
|
|
} else {
|
|
if (strlen(path)&&path[strlen(path)-1]=='\\') path[strlen(path)-1]=0;
|
|
if (strlen(path)&&path[strlen(path)-1]!=':') {
|
|
bool reset=false;
|
|
if(DOS_GetFileAttr(path,&attribute) && (attribute&DOS_ATTR_READ_ONLY)&&DOS_SetFileAttr(path, attribute & ~DOS_ATTR_READ_ONLY)) reset=true;
|
|
if (!DOS_RemoveDir(path)&&!(uselfn&&DOS_RemoveDir(sfull))) {
|
|
if (reset) DOS_SetFileAttr(path, attribute);
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_RMDIR_ERROR"),uselfn?sfull:full);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
tdirs.insert(tdirs.begin()+1, cdirs.begin(), cdirs.end());
|
|
return found;
|
|
}
|
|
|
|
void DOS_Shell::CMD_DELTREE(char * args) {
|
|
//HELP("DELTREE");
|
|
StripSpaces(args);
|
|
bool optY=ScanCMDBool(args,"Y");
|
|
char * rem=ScanCMDRemain(args);
|
|
if (rem) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
|
|
return;
|
|
}
|
|
if (!*args) {
|
|
WriteOut(MSG_Get("SHELL_MISSING_PARAMETER"));
|
|
return;
|
|
}
|
|
|
|
if (uselfn&&strchr(args, '*')) {
|
|
char * find_last;
|
|
find_last=strrchr_dbcs(args,'\\');
|
|
if (find_last==NULL) find_last=args;
|
|
else find_last++;
|
|
if (strlen(find_last)>0&&args[strlen(args)-1]=='*'&&strchr(find_last, '.')==NULL) strcat(args, ".*");
|
|
}
|
|
char buffer[CROSS_LEN];
|
|
args = ExpandDot(args,buffer, CROSS_LEN, true);
|
|
StripSpaces(args);
|
|
RealPt save_dta=dos.dta();
|
|
dos.dta(dos.tables.tempdta);
|
|
DOS_DTA dta(dos.dta());
|
|
tdirs.clear();
|
|
tdirs.emplace_back(std::string(args));
|
|
bool first=true, found=false;
|
|
ctrlbrk=false;
|
|
inshell=true;
|
|
while (!tdirs.empty()) {
|
|
if (doDeltree(this, (char *)tdirs.begin()->c_str(), dta, optY, first))
|
|
found=true;
|
|
first=false;
|
|
tdirs.erase(tdirs.begin());
|
|
}
|
|
inshell=true;
|
|
if (!found) WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args);
|
|
dos.dta(save_dta);
|
|
}
|
|
|
|
bool CheckBreak(DOS_Shell * shell) {
|
|
if (ctrlbrk || dos.errorcode == 77) {
|
|
if (dos.errorcode == 77) dos.errorcode = 0;
|
|
else if (ctrlbrk) {
|
|
ctrlbrk=false;
|
|
uint8_t c;uint16_t n=1;
|
|
DOS_ReadFile (STDIN,&c,&n);
|
|
if (c == 3 && (inshell || dos.errorcode == 77)) shell->WriteOut("^C\r\n");
|
|
if (dos.errorcode == 77) dos.errorcode = 0;
|
|
ctrlbrk=false;
|
|
}
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
bool cont[200];
|
|
static bool doTree(DOS_Shell * shell, char * args, DOS_DTA dta, bool optA, bool optF) {
|
|
char *p=strchr(args, ':');
|
|
bool found=false, last=false, plast=false;
|
|
int level=1;
|
|
if (p) {
|
|
*p=0;
|
|
if (*args=='-') {
|
|
plast=true;
|
|
args++;
|
|
}
|
|
level=atoi(args);
|
|
args=p+1;
|
|
if (tdirs.size()<2) last=true;
|
|
else {
|
|
char * arg=(char *)(tdirs.begin()+1)->c_str();
|
|
p=strchr(arg, ':');
|
|
if (p) {
|
|
*p=0;
|
|
if (level!=atoi(*arg=='-'?arg+1:arg)) last=true;
|
|
*p=':';
|
|
}
|
|
}
|
|
}
|
|
if (level>=200) return false;
|
|
char spath[DOS_PATHLENGTH],sargs[DOS_PATHLENGTH+4],path[DOS_PATHLENGTH+4],full[DOS_PATHLENGTH],sfull[DOS_PATHLENGTH+2];
|
|
if (!DOS_Canonicalize(args,full)||strrchr_dbcs(full,'\\')==NULL) { shell->WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));return level; }
|
|
if (!DOS_GetSFNPath(args,spath,false)) {
|
|
if (!level) shell->WriteOut(MSG_Get("SHELL_CMD_TREE_ERROR"));
|
|
return level;
|
|
}
|
|
if (!uselfn||!DOS_GetSFNPath(args,sfull,true)) strcpy(sfull,full);
|
|
if (level&&strlen(sfull)>4&&!strcasecmp(sfull+strlen(sfull)-4, "\\*.*")) {
|
|
*(sfull+strlen(sfull)-4)=0;
|
|
p=strrchr_dbcs(sfull, '\\');
|
|
char c=optA?(last?'\\':'+'):(last?0xc0:0xc3);
|
|
cont[level]=!last;
|
|
for (int i=1; i<level; i++) shell->WriteOut("%c ", cont[i]?(optA?'|':0xb3):' ');
|
|
shell->WriteOut(("%c"+std::string(3, optA?'-':0xc4)+"%s\n").c_str(), c, p?p+1:sfull);
|
|
*(sfull+strlen(sfull))='\\';
|
|
}
|
|
sprintf(sargs,"\"%s\"",spath);
|
|
bool res=DOS_FindFirst(sargs,0xffff & ~DOS_ATTR_VOLUME);
|
|
if (!res) {
|
|
if (!level) shell->WriteOut(MSG_Get("SHELL_CMD_TREE_ERROR"));
|
|
return level;
|
|
}
|
|
//uint16_t attribute=0; UNUSED
|
|
strcpy(path,full);
|
|
*(strrchr_dbcs(path,'\\')+1)=0;
|
|
char * end=strrchr_dbcs(full,'\\')+1;*end=0;
|
|
char * lend=strrchr_dbcs(sfull,'\\')+1;*lend=0;
|
|
char name[DOS_NAMELENGTH_ASCII],lname[LFN_NAMELENGTH+1];
|
|
uint32_t size,hsize;uint16_t time,date;uint8_t attr;uint16_t fattr;
|
|
std::vector<std::string> cdirs;
|
|
cdirs.clear();
|
|
while (res) {
|
|
if (CheckBreak(shell)) return false;
|
|
strcpy(spath,((plast||(level==1&&last)?"-":"")+std::to_string(level+1)+":").c_str());
|
|
strcat(spath, path);
|
|
dta.GetResult(name,lname,size,hsize,date,time,attr);
|
|
if (!((!strcmp(name, ".") || !strcmp(name, "..")) && attr & DOS_ATTR_DIRECTORY)) {
|
|
strcpy(end,name);
|
|
strcpy(lend,lname);
|
|
if (strlen(full)&&DOS_GetFileAttr(((uselfn||strchr(full, ' ')?(full[0]!='"'?"\"":""):"")+std::string(full)+(uselfn||strchr(full, ' ')?(full[strlen(full)-1]!='"'?"\"":""):"")).c_str(), &fattr)) {
|
|
if(attr&DOS_ATTR_DIRECTORY) {
|
|
if (strcmp(name, ".")&&strcmp(name, "..")) {
|
|
strcat(spath, name);
|
|
strcat(spath, "\\*.*");
|
|
cdirs.emplace_back(std::string(spath));
|
|
found=true;
|
|
}
|
|
} else if (optF) {
|
|
for (int i=1; i<=level; i++) shell->WriteOut("%c ", (i==1&&level>1?!plast:cont[i])?(optA?'|':0xb3):' ');
|
|
shell->WriteOut(" %s\n", uselfn?lname:name);
|
|
}
|
|
}
|
|
}
|
|
res=DOS_FindNext();
|
|
}
|
|
if (!found&&!level) {
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_TREE_ERROR"));
|
|
return false;
|
|
}
|
|
tdirs.insert(tdirs.begin()+1, cdirs.begin(), cdirs.end());
|
|
return true;
|
|
}
|
|
|
|
bool tree=false;
|
|
void DOS_Shell::CMD_TREE(char * args) {
|
|
//HELP("TREE");
|
|
StripSpaces(args);
|
|
bool optA=ScanCMDBool(args,"A");
|
|
bool optF=ScanCMDBool(args,"F");
|
|
char * rem=ScanCMDRemain(args);
|
|
if (rem) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
|
|
return;
|
|
}
|
|
StripSpaces(args);
|
|
char buffer[CROSS_LEN];
|
|
strcpy(buffer, "0:");
|
|
strcat(buffer, *args?args:".");
|
|
if (strlen(args)==2&&args[1]==':') strcat(buffer, ".");
|
|
if (args[strlen(args)-1]!='\\') strcat(buffer, "\\");
|
|
strcat(buffer, "*.*");
|
|
RealPt save_dta=dos.dta();
|
|
dos.dta(dos.tables.tempdta);
|
|
DOS_DTA dta(dos.dta());
|
|
if (strlen(args)>1&&args[1]==':') {
|
|
char c[]=" _:";
|
|
c[1]=toupper(args[0]);
|
|
if (!Drives[c[1]-'A']) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_DRIVE"));
|
|
return;
|
|
}
|
|
tree=true;
|
|
CMD_VOL(c[1]>='A'&&c[1]<='Z'?c:empty_string);
|
|
tree=false;
|
|
WriteOut("%c:%s\n", c[1], *args?args+2:".");
|
|
} else {
|
|
tree=true;
|
|
CMD_VOL(empty_string);
|
|
tree=false;
|
|
uint8_t drive=DOS_GetDefaultDrive();
|
|
WriteOut("%c:%s\n", 'A'+drive, *args?args:".");
|
|
}
|
|
for (int i=0; i<200; i++) cont[i]=false;
|
|
ctrlbrk=false;
|
|
inshell=true;
|
|
tdirs.clear();
|
|
tdirs.emplace_back(std::string(buffer));
|
|
while (!tdirs.empty()) {
|
|
if (!doTree(this, (char *)tdirs.begin()->c_str(), dta, optA, optF)) break;
|
|
tdirs.erase(tdirs.begin());
|
|
}
|
|
inshell=false;
|
|
dos.dta(save_dta);
|
|
}
|
|
|
|
void DOS_Shell::CMD_HELP(char * args){
|
|
HELP("HELP");
|
|
bool optall=ScanCMDBool(args,"A")|ScanCMDBool(args,"ALL");
|
|
/* Print the help */
|
|
args = trim(args);
|
|
upcase(args);
|
|
uint8_t attr = DOS_GetAnsiAttr();
|
|
if(!optall&&!*args) WriteOut(MSG_Get("SHELL_CMD_HELP"));
|
|
uint32_t cmd_index=0,write_count=0;
|
|
bool show=false;
|
|
while (cmd_list[cmd_index].name) {
|
|
if (optall || (*args && !strcmp(args, cmd_list[cmd_index].name)) || (!*args && !cmd_list[cmd_index].flags)) {
|
|
show=true;
|
|
if (*args && !strcmp(args, cmd_list[cmd_index].name) && !optall) {
|
|
std::string cmd=std::string(args);
|
|
if (cmd=="CD") cmd="CHDIR";
|
|
else if (cmd=="DEL"||cmd=="ERASE") cmd="DELETE";
|
|
else if (cmd=="LH") cmd="LOADHIGH";
|
|
else if (cmd=="MD") cmd="MKDIR";
|
|
else if (cmd=="RD") cmd="RMDIR";
|
|
else if (cmd=="REN") cmd="RENAME";
|
|
else if (cmd=="DX-CAPTURE") cmd="DXCAPTURE";
|
|
WriteOut("%s\n%s",MSG_Get(cmd_list[cmd_index].help), MSG_Get(("SHELL_CMD_" +cmd+ "_HELP_LONG").c_str()));
|
|
} else {
|
|
WriteOut("<\033[34;1m%-8s\033[0m> %s",cmd_list[cmd_index].name,MSG_Get(cmd_list[cmd_index].help));
|
|
if(!(++write_count%GetPauseCount())) {
|
|
WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
|
|
uint8_t c;uint16_t n=1;
|
|
DOS_ReadFile(STDIN,&c,&n);
|
|
if (c==3) {if (attr) DOS_SetAnsiAttr(attr);return;}
|
|
if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
|
|
}
|
|
}
|
|
}
|
|
cmd_index++;
|
|
}
|
|
if (optall&&show)
|
|
WriteOut(MSG_Get("SHELL_CMD_HELP_END1"));
|
|
else if (*args&&!show) {
|
|
std::string argc=std::string(StripArg(args));
|
|
if (argc!=""&&argc!="CWSDPMI") DoCommand((char *)(argc+(argc=="DOS4GW"||argc=="DOS32A"||argc=="ZIP"||argc=="UNZIP"?"":" /?")).c_str());
|
|
}
|
|
if (!*args&&show)
|
|
WriteOut(MSG_Get("SHELL_CMD_HELP_END2"));
|
|
if (attr) DOS_SetAnsiAttr(attr);
|
|
}
|
|
|
|
void removeChar(char *str, char c) {
|
|
char *src, *dst;
|
|
for (src = dst = str; *src != '\0'; src++) {
|
|
*dst = *src;
|
|
if (*dst != c) dst++;
|
|
}
|
|
*dst = '\0';
|
|
}
|
|
|
|
void DOS_Shell::CMD_RENAME(char * args){
|
|
HELP("RENAME");
|
|
StripSpaces(args);
|
|
char * rem=ScanCMDRemain(args);
|
|
if (rem) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
|
|
return;
|
|
}
|
|
if (!*args) {SyntaxError();return;}
|
|
char * arg1=StripArg(args);
|
|
StripSpaces(args);
|
|
if (!*args) {SyntaxError();return;}
|
|
char * arg2=StripArg(args);
|
|
StripSpaces(args);
|
|
if (*args) {SyntaxError();return;}
|
|
char* slash = strrchr_dbcs(arg1,'\\');
|
|
uint32_t size,hsize;uint16_t date;uint16_t time;uint8_t attr;
|
|
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];
|
|
//dir_source and target are introduced for when we support multiple files being renamed.
|
|
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
|
|
|
|
if (!slash) slash = strrchr(arg1,':');
|
|
if (slash) {
|
|
/* If directory specified (crystal caves installer)
|
|
* rename from c:\X : rename c:\abc.exe abc.shr.
|
|
* File must appear in C:\
|
|
* Ren X:\A\B C => ren X:\A\B X:\A\C */
|
|
|
|
//Copy first and then modify, makes GCC happy
|
|
safe_strncpy(dir_source,arg1,DOS_PATHLENGTH + 4);
|
|
char* dummy = strrchr_dbcs(dir_source,'\\');
|
|
if (!dummy) dummy = strrchr(dir_source,':');
|
|
if (!dummy) { //Possible due to length
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
|
|
return;
|
|
}
|
|
dummy++;
|
|
*dummy = 0;
|
|
if (strchr_dbcs(arg2,'\\')||strchr(arg2,':')) {
|
|
safe_strncpy(dir_target,arg2,DOS_PATHLENGTH + 4);
|
|
dummy = strrchr_dbcs(dir_target,'\\');
|
|
if (!dummy) dummy = strrchr(dir_target,':');
|
|
if (dummy) {
|
|
dummy++;
|
|
*dummy = 0;
|
|
if (strcasecmp(dir_source, dir_target)) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
|
|
return;
|
|
}
|
|
}
|
|
arg2=strrchr_dbcs(arg2,strrchr_dbcs(arg2,'\\')?'\\':':')+1;
|
|
}
|
|
if (strlen(dummy)&&dummy[strlen(dummy)-1]==':')
|
|
strcat(dummy, ".\\");
|
|
} else {
|
|
if (strchr_dbcs(arg2,'\\')||strchr(arg2,':')) {SyntaxError();return;};
|
|
strcpy(dir_source, ".\\");
|
|
}
|
|
|
|
strcpy(target,arg2);
|
|
|
|
char path[DOS_PATHLENGTH], spath[DOS_PATHLENGTH], pattern[DOS_PATHLENGTH], full[DOS_PATHLENGTH], *r;
|
|
if (!DOS_Canonicalize(arg1,full)) return;
|
|
r=strrchr_dbcs(full, '\\');
|
|
if (r!=NULL) {
|
|
*r=0;
|
|
strcpy(path, full);
|
|
strcat(path, "\\");
|
|
strcpy(pattern, r+1);
|
|
*r='\\';
|
|
} else {
|
|
strcpy(path, "");
|
|
strcpy(pattern, full);
|
|
}
|
|
int k=0;
|
|
for (int i=0;i<(int)strlen(pattern);i++)
|
|
if (pattern[i]!='\"')
|
|
pattern[k++]=pattern[i];
|
|
pattern[k]=0;
|
|
strcpy(spath, path);
|
|
if (strchr(arg1,'\"')||uselfn) {
|
|
if (!DOS_GetSFNPath(("\""+std::string(path)+"\\").c_str(), spath, false)) strcpy(spath, path);
|
|
if (!strlen(spath)||spath[strlen(spath)-1]!='\\') strcat(spath, "\\");
|
|
}
|
|
RealPt save_dta=dos.dta();
|
|
dos.dta(dos.tables.tempdta);
|
|
DOS_DTA dta(dos.dta());
|
|
std::string pfull=std::string(spath)+std::string(pattern);
|
|
int fbak=lfn_filefind_handle;
|
|
lfn_filefind_handle=uselfn?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE;
|
|
if (!DOS_FindFirst(((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)) {
|
|
lfn_filefind_handle=fbak;
|
|
WriteOut(MSG_Get("SHELL_CMD_RENAME_ERROR"),arg1);
|
|
} else {
|
|
std::vector<std::string> sources;
|
|
sources.clear();
|
|
|
|
do { /* File name and extension */
|
|
dta.GetResult(name,lname,size,hsize,date,time,attr);
|
|
lfn_filefind_handle=fbak;
|
|
|
|
if(!(attr&DOS_ATTR_DIRECTORY && (!strcmp(name, ".") || !strcmp(name, "..")))) {
|
|
strcpy(dir_target, target);
|
|
removeChar(dir_target, '\"');
|
|
arg2=dir_target;
|
|
strcpy(sargs, dir_source);
|
|
if (uselfn) removeChar(sargs, '\"');
|
|
strcat(sargs, uselfn?lname:name);
|
|
if (uselfn&&strchr(arg2,'*')&&!strchr(arg2,'.')) strcat(arg2, ".*");
|
|
char *dot1=strrchr(uselfn?lname:name,'.'), *dot2=strrchr(arg2,'.'), *star;
|
|
if (dot2==NULL) {
|
|
star=strchr(arg2,'*');
|
|
if (strchr(arg2,'?')) {
|
|
for (unsigned int i=0; i<(uselfn?LFN_NAMELENGTH:DOS_NAMELENGTH) && i<(star?star-arg2:strlen(arg2)); i++) {
|
|
if (*(arg2+i)=='?'&&i<strlen(name))
|
|
*(arg2+i)=name[i];
|
|
}
|
|
}
|
|
if (star) {
|
|
if ((unsigned int)(star-arg2)<strlen(name))
|
|
strcpy(star, name+(star-arg2));
|
|
else
|
|
*star=0;
|
|
}
|
|
removeChar(arg2, '?');
|
|
} else {
|
|
if (dot1) {
|
|
*dot1=0;
|
|
strcpy(tname1, uselfn?lname:name);
|
|
*dot1='.';
|
|
} else
|
|
strcpy(tname1, uselfn?lname:name);
|
|
*dot2=0;
|
|
strcpy(tname2, arg2);
|
|
*dot2='.';
|
|
star=strchr(tname2,'*');
|
|
if (strchr(tname2,'?')) {
|
|
for (unsigned int i=0; i<(uselfn?LFN_NAMELENGTH:DOS_NAMELENGTH) && i<(star?star-tname2:strlen(tname2)); i++) {
|
|
if (*(tname2+i)=='?'&&i<strlen(tname1))
|
|
*(tname2+i)=tname1[i];
|
|
}
|
|
}
|
|
if (star) {
|
|
if ((unsigned int)(star-tname2)<strlen(tname1))
|
|
strcpy(star, tname1+(star-tname2));
|
|
else
|
|
*star=0;
|
|
}
|
|
removeChar(tname2, '?');
|
|
if (dot1) {
|
|
strcpy(text1, dot1+1);
|
|
strcpy(text2, dot2+1);
|
|
star=strchr(text2,'*');
|
|
if (strchr(text2,'?')) {
|
|
for (unsigned int i=0; i<(uselfn?LFN_NAMELENGTH:DOS_NAMELENGTH) && i<(star?star-text2:strlen(text2)); i++) {
|
|
if (*(text2+i)=='?'&&i<strlen(text1))
|
|
*(text2+i)=text1[i];
|
|
}
|
|
}
|
|
if (star) {
|
|
if ((unsigned int)(star-text2)<strlen(text1))
|
|
strcpy(star, text1+(star-text2));
|
|
else
|
|
*star=0;
|
|
}
|
|
} else {
|
|
strcpy(text2, dot2+1);
|
|
if (strchr(text2,'?')||strchr(text2,'*')) {
|
|
for (unsigned int i=0; i<(uselfn?LFN_NAMELENGTH:DOS_NAMELENGTH) && i<(star?star-text2:strlen(text2)); i++) {
|
|
if (*(text2+i)=='*') {
|
|
*(text2+i)=0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
removeChar(text2, '?');
|
|
strcpy(tfull, tname2);
|
|
strcat(tfull, ".");
|
|
strcat(tfull, text2);
|
|
arg2=tfull;
|
|
}
|
|
strcpy(targs, dir_source);
|
|
if (uselfn) removeChar(targs, '\"');
|
|
strcat(targs, arg2);
|
|
sources.emplace_back(uselfn?((sargs[0]!='"'?"\"":"")+std::string(sargs)+(sargs[strlen(sargs)-1]!='"'?"\"":"")).c_str():sargs);
|
|
sources.emplace_back(uselfn?((targs[0]!='"'?"\"":"")+std::string(targs)+(targs[strlen(targs)-1]!='"'?"\"":"")).c_str():targs);
|
|
sources.emplace_back(strlen(sargs)>2&&sargs[0]=='.'&&sargs[1]=='\\'?sargs+2:sargs);
|
|
}
|
|
lfn_filefind_handle=uselfn?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE;
|
|
} while ( DOS_FindNext() );
|
|
lfn_filefind_handle=fbak;
|
|
if (sources.empty()) WriteOut(MSG_Get("SHELL_CMD_RENAME_ERROR"),arg1);
|
|
else {
|
|
for (std::vector<std::string>::iterator source = sources.begin(); source != sources.end(); ++source) {
|
|
char *oname=(char *)source->c_str();
|
|
source++;
|
|
if (source==sources.end()) break;
|
|
char *nname=(char *)source->c_str();
|
|
source++;
|
|
if (source==sources.end()||oname==NULL||nname==NULL) break;
|
|
char *fname=(char *)source->c_str();
|
|
if (!DOS_Rename(oname,nname)&&fname!=NULL)
|
|
WriteOut(MSG_Get("SHELL_CMD_RENAME_ERROR"),fname);
|
|
}
|
|
}
|
|
}
|
|
dos.dta(save_dta);
|
|
}
|
|
|
|
void DOS_Shell::CMD_ECHO(char * args){
|
|
if (!*args) {
|
|
if (echo) { WriteOut(MSG_Get("SHELL_CMD_ECHO_ON"));}
|
|
else { WriteOut(MSG_Get("SHELL_CMD_ECHO_OFF"));}
|
|
return;
|
|
}
|
|
char buffer[512];
|
|
char* pbuffer = buffer;
|
|
safe_strncpy(buffer,args,512);
|
|
StripSpaces(pbuffer);
|
|
if (strcasecmp(pbuffer,"OFF")==0) {
|
|
echo=false;
|
|
return;
|
|
}
|
|
if (strcasecmp(pbuffer,"ON")==0) {
|
|
echo=true;
|
|
return;
|
|
}
|
|
if(strcasecmp(pbuffer,"/?")==0) { HELP("ECHO"); }
|
|
|
|
args++;//skip first character. either a slash or dot or space
|
|
size_t len = strlen(args); //TODO check input of else ook nodig is.
|
|
if(len && args[len - 1] == '\r') {
|
|
LOG(LOG_MISC,LOG_WARN)("Hu ? carriage return already present. Is this possible?");
|
|
WriteOut_NoParsing(args, true);
|
|
WriteOut("\n");
|
|
} else {
|
|
WriteOut_NoParsing(args, true);
|
|
WriteOut("\r\n");
|
|
}
|
|
}
|
|
|
|
void DOS_Shell::CMD_EXIT(char * args) {
|
|
HELP("EXIT");
|
|
exit = true;
|
|
}
|
|
|
|
std::vector<uint8_t> olddrives;
|
|
std::vector<std::string> olddirs;
|
|
void DOS_Shell::CMD_PUSHD(char * args) {
|
|
HELP("PUSHD");
|
|
StripSpaces(args);
|
|
char sargs[CROSS_LEN];
|
|
if (strlen(args)>1 && args[1]==':' && toupper(args[0])>='A' && toupper(args[0])<='Z' && !Drives[toupper(args[0])-'A']) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_DRIVE"));
|
|
return;
|
|
}
|
|
if (*args && !DOS_GetSFNPath(args,sargs,false)) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
|
|
return;
|
|
}
|
|
if (*args) {
|
|
char dir[DOS_PATHLENGTH];
|
|
uint8_t drive = DOS_GetDefaultDrive()+'A';
|
|
DOS_GetCurrentDir(0,dir,true);
|
|
if (strlen(args)>1 && args[1]==':') DOS_SetDefaultDrive(toupper(args[0])-'A');
|
|
if (DOS_ChangeDir(sargs)) {
|
|
olddrives.push_back(drive);
|
|
olddirs.emplace_back(std::string(dir));
|
|
} else {
|
|
if (strlen(args)>1 && args[1]==':') DOS_SetDefaultDrive(drive-'A');
|
|
WriteOut(MSG_Get("SHELL_CMD_CHDIR_ERROR"),args);
|
|
}
|
|
} else {
|
|
for (int i=(int)(olddrives.size()-1); i>=0; i--)
|
|
if (olddrives.at(i)>='A'&&olddrives.at(i)<='Z')
|
|
WriteOut("%c:\\%s\n",olddrives.at(i),olddirs.at(i).c_str());
|
|
}
|
|
}
|
|
|
|
void DOS_Shell::CMD_POPD(char * args) {
|
|
HELP("POPD");
|
|
if (!olddrives.size()) return;
|
|
uint8_t olddrive=olddrives.back();
|
|
std::string olddir=olddirs.back();
|
|
if (olddrive>='A'&&olddrive<='Z'&&Drives[olddrive-'A']) {
|
|
uint8_t drive = DOS_GetDefaultDrive()+'A';
|
|
if (olddrive!=DOS_GetDefaultDrive()+'A') DOS_SetDefaultDrive(olddrive-'A');
|
|
if (Drives[DOS_GetDefaultDrive()]->TestDir(olddir.c_str()))
|
|
strcpy(Drives[DOS_GetDefaultDrive()]->curdir,olddir.c_str());
|
|
else
|
|
DOS_SetDefaultDrive(drive-'A');
|
|
}
|
|
olddrives.pop_back();
|
|
olddirs.pop_back();
|
|
}
|
|
|
|
void DOS_Shell::CMD_CHDIR(char * args) {
|
|
HELP("CHDIR");
|
|
StripSpaces(args);
|
|
char sargs[CROSS_LEN];
|
|
if (*args && !DOS_GetSFNPath(args,sargs,false)) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
|
|
return;
|
|
}
|
|
uint8_t drive = DOS_GetDefaultDrive()+'A';
|
|
char dir[DOS_PATHLENGTH];
|
|
if (!*args) {
|
|
DOS_GetCurrentDir(0,dir,true);
|
|
WriteOut("%c:\\",drive);
|
|
WriteOut_NoParsing(dir, true);
|
|
WriteOut("\n");
|
|
} else if(strlen(args) == 2 && args[1]==':') {
|
|
uint8_t targetdrive = (args[0] | 0x20)-'a' + 1;
|
|
unsigned char targetdisplay = *reinterpret_cast<unsigned char*>(&args[0]);
|
|
if(!DOS_GetCurrentDir(targetdrive,dir,true)) { // verify that this should be true
|
|
if(drive == 'Z') {
|
|
WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_NOT_FOUND"),toupper(targetdisplay));
|
|
} else {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
|
|
}
|
|
return;
|
|
}
|
|
WriteOut("%c:\\",toupper(targetdisplay));
|
|
WriteOut_NoParsing(dir, true);
|
|
WriteOut("\n");
|
|
if(drive == 'Z')
|
|
WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT"),toupper(targetdisplay));
|
|
} else if (!DOS_ChangeDir(sargs)) {
|
|
/* Changedir failed. Check if the filename is longer then 8 and/or contains spaces */
|
|
|
|
std::string temps(args),slashpart;
|
|
std::string::size_type separator = temps.find_first_of("\\/");
|
|
if(!separator) {
|
|
slashpart = temps.substr(0,1);
|
|
temps.erase(0,1);
|
|
}
|
|
separator = temps.find_first_of("\"");
|
|
if(separator != std::string::npos) temps.erase(separator);
|
|
separator = temps.rfind('.');
|
|
if(separator != std::string::npos) temps.erase(separator);
|
|
separator = temps.find(' ');
|
|
if(separator != std::string::npos) {/* Contains spaces */
|
|
temps.erase(separator);
|
|
if(temps.size() >6) temps.erase(6);
|
|
temps += "~1";
|
|
WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT_2"),temps.insert(0,slashpart).c_str());
|
|
} else {
|
|
if (drive == 'Z') {
|
|
WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT_3"));
|
|
} else {
|
|
WriteOut(MSG_Get("SHELL_CMD_CHDIR_ERROR"),args);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DOS_Shell::CMD_MKDIR(char * args) {
|
|
HELP("MKDIR");
|
|
StripSpaces(args);
|
|
char * rem=ScanCMDRemain(args);
|
|
if (rem) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
|
|
return;
|
|
}
|
|
if (!*args) {
|
|
WriteOut(MSG_Get("SHELL_MISSING_PARAMETER"));
|
|
return;
|
|
}
|
|
if (!DOS_MakeDir(args)) {
|
|
WriteOut(MSG_Get(dos.errorcode==DOSERR_ACCESS_DENIED?"SHELL_CMD_MKDIR_EXIST":"SHELL_CMD_MKDIR_ERROR"),args);
|
|
}
|
|
}
|
|
|
|
void DOS_Shell::CMD_RMDIR(char * args) {
|
|
HELP("RMDIR");
|
|
// ignore /s,and /q switches for compatibility
|
|
ScanCMDBool(args,"S");
|
|
ScanCMDBool(args,"Q");
|
|
StripSpaces(args);
|
|
char * rem=ScanCMDRemain(args);
|
|
if (rem) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
|
|
return;
|
|
}
|
|
if (!*args) {
|
|
WriteOut(MSG_Get("SHELL_MISSING_PARAMETER"));
|
|
return;
|
|
}
|
|
if (!DOS_RemoveDir(args)) {
|
|
WriteOut(MSG_Get("SHELL_CMD_RMDIR_ERROR"),args);
|
|
}
|
|
}
|
|
|
|
static void FormatNumber(uint64_t num,char * buf) {
|
|
uint64_t numo = num;
|
|
uint32_t numb,numk,numm,numg,nummi,numgi,numti,numpi,numei;
|
|
numb=num % 1000;
|
|
num/=1000;
|
|
numk=num % 1000;
|
|
num/=1000;
|
|
numm=num % 1000;
|
|
num/=1000;
|
|
numg=num % 1000;
|
|
numo/=1024;
|
|
numo/=1024;
|
|
nummi=(numo % 1024) / 10.24 + 0.5;
|
|
numo/=1024;
|
|
numgi=numo % 1000;
|
|
numo/=1000;
|
|
numti=numo % 1000;
|
|
numo/=1000;
|
|
numpi=numo % 1000;
|
|
numei=numo / 1000;
|
|
if (numei) {
|
|
sprintf(buf,"%u%c%03u%c%03u%c%03u%c%02u G",numei,dos.tables.country[7],numpi,dos.tables.country[7],numti,dos.tables.country[7],numgi,dos.tables.country[9],nummi);
|
|
return;
|
|
}
|
|
if (numpi) {
|
|
sprintf(buf,"%u%c%03u%c%03u%c%02u G",numpi,dos.tables.country[7],numti,dos.tables.country[7],numgi,dos.tables.country[9],nummi);
|
|
return;
|
|
}
|
|
if (numti) {
|
|
sprintf(buf,"%u%c%03u%c%02u G",numti,dos.tables.country[7],numgi,dos.tables.country[9],nummi);
|
|
return;
|
|
}
|
|
if (numg) {
|
|
if (numgi>127) sprintf(buf,"%u%c%02u G",numgi,dos.tables.country[9],nummi);
|
|
else 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);
|
|
return;
|
|
}
|
|
if (numm) {
|
|
sprintf(buf,"%u%c%03u%c%03u",numm,dos.tables.country[7],numk,dos.tables.country[7],numb);
|
|
return;
|
|
}
|
|
if (numk) {
|
|
sprintf(buf,"%u%c%03u",numk,dos.tables.country[7],numb);
|
|
return;
|
|
}
|
|
sprintf(buf,"%u",numb);
|
|
}
|
|
|
|
char buffer[15] = {0};
|
|
char *FormatDate(uint16_t year, uint8_t month, uint8_t day) {
|
|
char formatstring[6], c=dos.tables.country[11];
|
|
sprintf(formatstring, dos.tables.country[0]==1?"D%cM%cY":(dos.tables.country[0]==2?"Y%cM%cD":"M%cD%cY"), c, c);
|
|
Bitu bufferptr=0;
|
|
for(Bitu i = 0; i < 5; i++) {
|
|
if(i==1 || i==3) {
|
|
buffer[bufferptr] = formatstring[i];
|
|
bufferptr++;
|
|
} else {
|
|
if(formatstring[i]=='M') bufferptr += (Bitu)sprintf(buffer+bufferptr,"%02u", month);
|
|
if(formatstring[i]=='D') bufferptr += (Bitu)sprintf(buffer+bufferptr,"%02u", day);
|
|
if(formatstring[i]=='Y') bufferptr += (Bitu)sprintf(buffer+bufferptr,"%04u", year);
|
|
}
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
char *FormatTime(Bitu hour, Bitu min, Bitu sec, Bitu msec) {
|
|
Bitu fhour=hour;
|
|
static char retBuf[14];
|
|
char ampm[3]="";
|
|
if (!(dos.tables.country[17]&1)) { // 12 hour notation?
|
|
if (hour!=12)
|
|
hour %= 12;
|
|
strcpy(ampm, hour != 12 && hour == fhour ? "am" : "pm");
|
|
}
|
|
char sep = dos.tables.country[13];
|
|
if (sec>=100&&msec>=100)
|
|
sprintf(retBuf, "%2u%c%02u%c", (unsigned int)hour, sep, (unsigned int)min, *ampm);
|
|
else
|
|
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);
|
|
return retBuf;
|
|
}
|
|
|
|
uint64_t byte_count;
|
|
uint32_t file_count,dir_count;
|
|
Bitu p_count;
|
|
std::vector<std::string> dirs, adirs;
|
|
static bool dirPaused(DOS_Shell * shell, Bitu w_size, bool optP, bool optW, bool show=true) {
|
|
p_count+=optW?5:1;
|
|
if (optP && p_count%(GetPauseCount()*w_size)<1) {
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
|
|
uint8_t c;uint16_t n=1;
|
|
DOS_ReadFile(STDIN,&c,&n);
|
|
if (c==3) {if (show) shell->WriteOut("^C\r\n");return false;}
|
|
if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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 optOGN, bool optOGD, bool optOGE, bool optOGS, bool optOG, bool optON, bool optOD, bool optOE, bool optOS, bool reverseSort, bool rev2Sort) {
|
|
char path[DOS_PATHLENGTH];
|
|
char sargs[CROSS_LEN], largs[CROSS_LEN], buffer[CROSS_LEN];
|
|
unsigned int tcols=IS_PC98_ARCH?80:real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
|
|
if (!tcols) tcols=80;
|
|
|
|
/* Make a full path in the args */
|
|
if (!DOS_Canonicalize(args,path)) {
|
|
shell->WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
|
|
return true;
|
|
}
|
|
*(strrchr_dbcs(path,'\\')+1)=0;
|
|
if (!DOS_GetSFNPath(path,sargs,false)) {
|
|
shell->WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
|
|
return true;
|
|
}
|
|
if (!optB&&!optS) {
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_DIR_INTRO"),uselfn&&!optZ&&DOS_GetSFNPath(path,largs,true)?largs:sargs);
|
|
if (optP) {
|
|
p_count+=optW?10:2;
|
|
if (p_count%(GetPauseCount()*w_size)<2) {
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
|
|
uint8_t c;uint16_t n=1;
|
|
DOS_ReadFile(STDIN,&c,&n);
|
|
if (c==3) return false;
|
|
if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
|
|
}
|
|
}
|
|
}
|
|
if (*(sargs+strlen(sargs)-1) != '\\') strcat(sargs,"\\");
|
|
|
|
uint64_t cbyte_count=0;
|
|
uint32_t cfile_count=0,w_count=0;
|
|
int fbak=lfn_filefind_handle;
|
|
lfn_filefind_handle=uselfn&&!optZ?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE;
|
|
bool ret=DOS_FindFirst(args,0xffff & ~DOS_ATTR_VOLUME), found=true, first=true;
|
|
if (!ret) {
|
|
size_t len = strlen(args);
|
|
args = ExpandDotMore(args,buffer,CROSS_LEN);
|
|
if (strlen(args)!=len) ret=DOS_FindFirst(args,0xffff & ~DOS_ATTR_VOLUME);
|
|
}
|
|
lfn_filefind_handle=fbak;
|
|
if (ret) {
|
|
std::vector<DtaResult> results;
|
|
|
|
lfn_filefind_handle=uselfn&&!optZ?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE;
|
|
do { /* File name and extension */
|
|
DtaResult result;
|
|
dta.GetResult(result.name,result.lname,result.size,result.hsize,result.date,result.time,result.attr);
|
|
|
|
/* Skip non-directories if option AD is present, or skip dirs in case of A-D */
|
|
if(optAD && !(result.attr&DOS_ATTR_DIRECTORY) ) continue;
|
|
else if(optAminusD && (result.attr&DOS_ATTR_DIRECTORY) ) continue;
|
|
else if(optAS && !(result.attr&DOS_ATTR_SYSTEM) ) continue;
|
|
else if(optAminusS && (result.attr&DOS_ATTR_SYSTEM) ) continue;
|
|
else if(optAH && !(result.attr&DOS_ATTR_HIDDEN) ) continue;
|
|
else if(optAminusH && (result.attr&DOS_ATTR_HIDDEN) ) continue;
|
|
else if(optAR && !(result.attr&DOS_ATTR_READ_ONLY) ) continue;
|
|
else if(optAminusR && (result.attr&DOS_ATTR_READ_ONLY) ) continue;
|
|
else if(optAA && !(result.attr&DOS_ATTR_ARCHIVE) ) continue;
|
|
else if(optAminusA && (result.attr&DOS_ATTR_ARCHIVE) ) continue;
|
|
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;
|
|
|
|
results.push_back(result);
|
|
|
|
} while ( (ret=DOS_FindNext()) );
|
|
lfn_filefind_handle=fbak;
|
|
|
|
bool oneRev = (reverseSort||rev2Sort)&&reverseSort!=rev2Sort;
|
|
if (optON) {
|
|
// Sort by name
|
|
std::sort(results.begin(), results.end(), DtaResult::compareName);
|
|
} else if (optOE) {
|
|
// Sort by extension
|
|
std::sort(results.begin(), results.end(), DtaResult::compareExt);
|
|
} else if (optOD) {
|
|
// Sort by date
|
|
std::sort(results.begin(), results.end(), DtaResult::compareDate);
|
|
} else if (optOS) {
|
|
// Sort by size
|
|
std::sort(results.begin(), results.end(), DtaResult::compareSize);
|
|
} else if (optOG) {
|
|
// Directories first, then files
|
|
std::sort(results.begin(), results.end(), DtaResult::groupDirs);
|
|
} else if (optOGN) {
|
|
// Directories first, then files, both sort by name
|
|
std::sort(results.begin(), results.end(), oneRev?DtaResult::groupRevDef:DtaResult::groupDef);
|
|
} else if (optOGE) {
|
|
// Directories first, then files, both sort by extension
|
|
std::sort(results.begin(), results.end(), oneRev?DtaResult::groupRevExt:DtaResult::groupExt);
|
|
} else if (optOGS) {
|
|
// Directories first, then files, both sort by size
|
|
std::sort(results.begin(), results.end(), oneRev?DtaResult::groupRevSize:DtaResult::groupSize);
|
|
} else if (optOGD) {
|
|
// Directories first, then files, both sort by date
|
|
std::sort(results.begin(), results.end(), oneRev?DtaResult::groupRevDate:DtaResult::groupDate);
|
|
}
|
|
if (reverseSort) std::reverse(results.begin(), results.end());
|
|
|
|
for (std::vector<DtaResult>::iterator iter = results.begin(); iter != results.end(); ++iter) {
|
|
if (CheckBreak(shell)) return false;
|
|
char * name = iter->name;
|
|
char *lname = iter->lname;
|
|
uint32_t size = iter->size;
|
|
uint32_t hsize = iter->hsize;
|
|
uint16_t date = iter->date;
|
|
uint16_t time = iter->time;
|
|
uint8_t attr = iter->attr;
|
|
|
|
/* output the file */
|
|
if (optB) {
|
|
// this overrides pretty much everything
|
|
if (strcmp(".",uselfn&&!optZ?lname:name) && strcmp("..",uselfn&&!optZ?lname:name)) {
|
|
int m=shell->WriteOut_NoParsing(uselfn&&!optZ?lname:name, true);
|
|
shell->WriteOut("\n");
|
|
if (optP) {
|
|
p_count+=m+1;
|
|
if (p_count%GetPauseCount()<m+1) {
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
|
|
uint8_t c;uint16_t n=1;
|
|
DOS_ReadFile(STDIN,&c,&n);
|
|
if (c==3) return false;
|
|
if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
|
|
p_count=0;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (first&&optS) {
|
|
first=false;
|
|
shell->WriteOut("\n");
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_DIR_INTRO"),uselfn&&!optZ&&DOS_GetSFNPath(path,largs,true)?largs:sargs);
|
|
if (optP) {
|
|
p_count+=optW?15:3;
|
|
if (optS&&p_count%(GetPauseCount()*w_size)<3) {
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
|
|
uint8_t c;uint16_t n=1;
|
|
DOS_ReadFile(STDIN,&c,&n);
|
|
if (c==3) return false;
|
|
if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
|
|
}
|
|
}
|
|
}
|
|
char * ext = empty_string;
|
|
if (!optW && (name[0] != '.')) {
|
|
ext = strrchr(name, '.');
|
|
if (!ext) ext = empty_string;
|
|
else *ext++ = 0;
|
|
}
|
|
uint8_t day = (uint8_t)(date & 0x001f);
|
|
uint8_t month = (uint8_t)((date >> 5) & 0x000f);
|
|
uint16_t year = (uint16_t)((date >> 9) + 1980);
|
|
uint8_t hour = (uint8_t)((time >> 5 ) >> 6);
|
|
uint8_t minute = (uint8_t)((time >> 5) & 0x003f);
|
|
unsigned int m=0;
|
|
if (attr & DOS_ATTR_DIRECTORY) {
|
|
if (optW) {
|
|
shell->WriteOut("[%s]",name);
|
|
size_t namelen = strlen(name);
|
|
if (namelen <= 14) {
|
|
for (size_t i=14-namelen;i>0;i--) shell->WriteOut(" ");
|
|
}
|
|
} else {
|
|
shell->WriteOut("%-8s %-3s %-16s %s %s",name,ext,"<DIR>",FormatDate(year,month,day),FormatTime(hour,minute,100,100));
|
|
if (uselfn&&!optZ) {
|
|
shell->WriteOut(" ");
|
|
m=shell->WriteOut_NoParsing(lname, true);
|
|
}
|
|
shell->WriteOut("\n");
|
|
if (optP) {
|
|
p_count+=(optW?5:1)*m;
|
|
if (p_count%(GetPauseCount()*w_size)<m) {
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
|
|
uint8_t c;uint16_t n=1;
|
|
DOS_ReadFile(STDIN,&c,&n);
|
|
if (c==3) return false;
|
|
if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
|
|
p_count=0;
|
|
}
|
|
}
|
|
}
|
|
dir_count++;
|
|
} else {
|
|
if (optW) {
|
|
shell->WriteOut("%-16s",name);
|
|
} else {
|
|
FormatNumber(size+hsize*0x100000000,numformat);
|
|
shell->WriteOut("%-8s %-3s %16s %s %s",name,ext,numformat,FormatDate(year,month,day),FormatTime(hour,minute,100,100));
|
|
if (uselfn&&!optZ) {
|
|
shell->WriteOut(" ");
|
|
m=shell->WriteOut_NoParsing(lname, true);
|
|
}
|
|
shell->WriteOut("\n");
|
|
if (optP) {
|
|
p_count+=(optW?5:1)*m;
|
|
if (p_count%(GetPauseCount()*w_size)<m) {
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
|
|
uint8_t c;uint16_t n=1;
|
|
DOS_ReadFile(STDIN,&c,&n);
|
|
if (c==3) return false;
|
|
if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
|
|
p_count=0;
|
|
}
|
|
}
|
|
}
|
|
if (optS) {
|
|
cfile_count++;
|
|
cbyte_count+=size+hsize*0x100000000;
|
|
}
|
|
file_count++;
|
|
byte_count+=size+hsize*0x100000000;
|
|
}
|
|
if (optW) w_count++;
|
|
}
|
|
if (optW && w_count%5==0 && tcols>80) shell->WriteOut("\n");
|
|
if (optP && !optB && !(++p_count%(GetPauseCount()*w_size))) {
|
|
if (optW&&w_count%5) {shell->WriteOut("\n");w_count=0;}
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
|
|
uint8_t c;uint16_t n=1;
|
|
DOS_ReadFile(STDIN,&c,&n);
|
|
if (c==3) return false;
|
|
if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
|
|
}
|
|
}
|
|
|
|
if (!results.size())
|
|
found=false;
|
|
else if (optW&&w_count%5)
|
|
shell->WriteOut("\n");
|
|
} else
|
|
found=false;
|
|
if (!found&&!optB&&!optS) {
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args);
|
|
if (!dirPaused(shell, w_size, optP, optW, false)) return false;
|
|
}
|
|
if (optS) {
|
|
size_t len=strlen(sargs);
|
|
strcat(sargs, "*.*");
|
|
bool ret=DOS_FindFirst(sargs,0xffff & ~DOS_ATTR_VOLUME);
|
|
*(sargs+len)=0;
|
|
if (ret) {
|
|
std::vector<std::string> cdirs;
|
|
cdirs.clear();
|
|
do { /* File name and extension */
|
|
DtaResult result;
|
|
dta.GetResult(result.name,result.lname,result.size,result.hsize,result.date,result.time,result.attr);
|
|
|
|
if(result.attr&DOS_ATTR_DIRECTORY && strcmp(result.name, ".")&&strcmp(result.name, "..")) {
|
|
strcat(sargs, result.name);
|
|
strcat(sargs, "\\");
|
|
char *fname = strrchr_dbcs(args, '\\');
|
|
if (fname==NULL) fname=args;
|
|
else fname++;
|
|
strcat(sargs, fname);
|
|
cdirs.push_back((sargs[0]!='"'&&sargs[strlen(sargs)-1]=='"'?"\"":"")+std::string(sargs));
|
|
*(sargs+len)=0;
|
|
}
|
|
} while ( (ret=DOS_FindNext()) );
|
|
dirs.insert(dirs.begin()+1, cdirs.begin(), cdirs.end());
|
|
}
|
|
if (found&&!optB) {
|
|
FormatNumber(cbyte_count,numformat);
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_DIR_BYTES_USED"),cfile_count,numformat);
|
|
if (!dirPaused(shell, w_size, optP, optW, false)) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void DOS_Shell::CMD_DIR(char * args) {
|
|
HELP("DIR");
|
|
char numformat[64];
|
|
char path[DOS_PATHLENGTH];
|
|
char sargs[CROSS_LEN];
|
|
|
|
std::string line;
|
|
if(GetEnvStr("DIRCMD",line)){
|
|
std::string::size_type idx = line.find('=');
|
|
std::string value=line.substr(idx +1 , std::string::npos);
|
|
line = std::string(args) + " " + value;
|
|
args=const_cast<char*>(line.c_str());
|
|
}
|
|
|
|
ScanCMDBool(args,"4"); /* /4 ignored (default) */
|
|
bool optW=ScanCMDBool(args,"W");
|
|
bool optP=ScanCMDBool(args,"P");
|
|
if (ScanCMDBool(args,"WP") || ScanCMDBool(args,"PW")) optW=optP=true;
|
|
if (ScanCMDBool(args,"-W")) optW=false;
|
|
if (ScanCMDBool(args,"-P")) optP=false;
|
|
bool optZ=ScanCMDBool(args,"Z");
|
|
if (ScanCMDBool(args,"-Z")) optZ=false;
|
|
bool optS=ScanCMDBool(args,"S");
|
|
if (ScanCMDBool(args,"-S")) optS=false;
|
|
bool optB=ScanCMDBool(args,"B");
|
|
if (ScanCMDBool(args,"-B")) optB=false;
|
|
bool optA=ScanCMDBool(args,"A");
|
|
bool optAD=ScanCMDBool(args,"AD")||ScanCMDBool(args,"A:D");
|
|
bool optAminusD=ScanCMDBool(args,"A-D")||ScanCMDBool(args,"A:-D");
|
|
bool optAS=ScanCMDBool(args,"AS")||ScanCMDBool(args,"A:S");
|
|
bool optAminusS=ScanCMDBool(args,"A-S")||ScanCMDBool(args,"A:-S");
|
|
bool optAH=ScanCMDBool(args,"AH")||ScanCMDBool(args,"A:H");
|
|
bool optAminusH=ScanCMDBool(args,"A-H")||ScanCMDBool(args,"A:-H");
|
|
bool optAR=ScanCMDBool(args,"AR")||ScanCMDBool(args,"A:R");
|
|
bool optAminusR=ScanCMDBool(args,"A-R")||ScanCMDBool(args,"A:-R");
|
|
bool optAA=ScanCMDBool(args,"AA")||ScanCMDBool(args,"A:A");
|
|
bool optAminusA=ScanCMDBool(args,"A-A")||ScanCMDBool(args,"A:-A");
|
|
if (ScanCMDBool(args,"-A")) {
|
|
optA = false;
|
|
optAD = false;
|
|
optAminusD = false;
|
|
optAS = false;
|
|
optAminusS = false;
|
|
optAH = false;
|
|
optAminusH = false;
|
|
optAR = false;
|
|
optAminusR = false;
|
|
optAA = false;
|
|
optAminusA = false;
|
|
}
|
|
// Sorting flags
|
|
bool reverseSort = false, rev2Sort = false;
|
|
bool optON=ScanCMDBool(args,"ON")||ScanCMDBool(args,"O:N");
|
|
if (ScanCMDBool(args,"O-N")||ScanCMDBool(args,"O:-N")) {
|
|
optON = true;
|
|
reverseSort = true;
|
|
}
|
|
bool optOD=ScanCMDBool(args,"OD")||ScanCMDBool(args,"O:D");
|
|
if (ScanCMDBool(args,"O-D")||ScanCMDBool(args,"O:-D")) {
|
|
optOD = true;
|
|
reverseSort = true;
|
|
}
|
|
bool optOE=ScanCMDBool(args,"OE")||ScanCMDBool(args,"O:E");
|
|
if (ScanCMDBool(args,"O-E")||ScanCMDBool(args,"O:-E")) {
|
|
optOE = true;
|
|
reverseSort = true;
|
|
}
|
|
bool optOS=ScanCMDBool(args,"OS")||ScanCMDBool(args,"O:S");
|
|
if (ScanCMDBool(args,"O-S")||ScanCMDBool(args,"O:-S")) {
|
|
optOS = true;
|
|
reverseSort = true;
|
|
}
|
|
bool optOG=ScanCMDBool(args,"OG")||ScanCMDBool(args,"O:G");
|
|
if (ScanCMDBool(args,"O-G")||ScanCMDBool(args,"O:-G")) {
|
|
optOG = true;
|
|
reverseSort = true;
|
|
}
|
|
bool b0 = false, b1 = false, b2 = false, b3 = false;
|
|
bool optOGN = false, optOGD = false, optOGE = false, optOGS = false;
|
|
b0=ScanCMDBool(args,"O")||ScanCMDBool(args,"OGN")||ScanCMDBool(args,"O:GN");b1=ScanCMDBool(args,"O-GN")||ScanCMDBool(args,"O:-GN");
|
|
b2=ScanCMDBool(args,"O-G-N")||ScanCMDBool(args,"O:-G-N"),b3=ScanCMDBool(args,"OG-N")||ScanCMDBool(args,"O:G-N");
|
|
if (b0||b1||b2||b3) {
|
|
optOGN = true;
|
|
reverseSort = b1||b2;
|
|
rev2Sort = b2||b3;
|
|
}
|
|
b0=ScanCMDBool(args,"OGD")||ScanCMDBool(args,"O:GD");b1=ScanCMDBool(args,"O-GD")||ScanCMDBool(args,"O:-GD");
|
|
b2=ScanCMDBool(args,"O-G-D")||ScanCMDBool(args,"O:-G-D");b3=ScanCMDBool(args,"OG-D")||ScanCMDBool(args,"O:G-D");
|
|
if (b0||b1||b2||b3) {
|
|
optOGD = true;
|
|
reverseSort = b1||b2;
|
|
rev2Sort = b2||b3;
|
|
}
|
|
b0=ScanCMDBool(args,"OGE")||ScanCMDBool(args,"O:GE");b1=ScanCMDBool(args,"O-GE")||ScanCMDBool(args,"O:-GE");
|
|
b2=ScanCMDBool(args,"O-G-E")||ScanCMDBool(args,"O:-G-E");b3=ScanCMDBool(args,"OG-E")||ScanCMDBool(args,"O:G-E");
|
|
if (b0||b1||b2||b3) {
|
|
optOGE = true;
|
|
reverseSort = b1||b2;
|
|
rev2Sort = b2||b3;
|
|
}
|
|
b0=ScanCMDBool(args,"OGS")||ScanCMDBool(args,"O:GS");b1=ScanCMDBool(args,"O-GS")||ScanCMDBool(args,"O:-GS");
|
|
b2=ScanCMDBool(args,"O-G-S")||ScanCMDBool(args,"O:-G-S");b3=ScanCMDBool(args,"OG-S")||ScanCMDBool(args,"O:G-S");
|
|
if (b0||b1||b2||b3) {
|
|
optOGS = true;
|
|
reverseSort = b1||b2;
|
|
rev2Sort = b2||b3;
|
|
}
|
|
if (optOGN||optOGD||optOGE||optOGS) optOG = false;
|
|
if (ScanCMDBool(args,"-O")) {
|
|
optOG = false;
|
|
optON = false;
|
|
optOD = false;
|
|
optOE = false;
|
|
optOS = false;
|
|
optOGN = false;
|
|
optOGD = false;
|
|
optOGE = false;
|
|
optOGS = false;
|
|
reverseSort = false;
|
|
}
|
|
const char *valid[] = {"4","W","P","-W","-P","WP","PW","Z","-Z","S","-S","B","-B",
|
|
"A","-A","AD","A:D","A-D","A:-D","AS","A:S","A-S","A:-S","AH","A:H","A-H","A:-H","AR","A:R","A-R","A:-R","AA","A:A","A-A","A:-A",
|
|
"O","-O","ON","O:N","O-N","O:-N","OD","O:D","O-D","O:-D","OE","O:E","O-E","O:-E","OS","O:S","O-S","O:-S","OG","O:G","O-G","O:-G",
|
|
"OGN","O:GN","O-GN","O:-GN","OG-N","O:G-N","O-G-N","O:-G-N","OGD","O:GD","O-GD","O:-GD","OG-D","O:G-D","O-G-D","O:-G-D",
|
|
"OGE","O:GE","O-GE","O:-GE","OG-E","O:G-E","O-G-E","O:-G-E","OGS","O:GS","O-GS","O:-GS","OG-S","O:G-S","O-G-S","O:-G-S",
|
|
NULL};
|
|
if (args && strlen(args)>1) for (int i=0; valid[i] && *args && strchr(args,'/'); i++) while (ScanCMDBool(args,valid[i]));
|
|
char * rem=ScanCMDRemain(args);
|
|
if (rem) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
|
|
return;
|
|
}
|
|
byte_count=0;file_count=0;dir_count=0;p_count=0;
|
|
Bitu w_size = optW?5:1;
|
|
|
|
char buffer[CROSS_LEN];
|
|
args = trim(args);
|
|
size_t argLen = strlen(args);
|
|
if (argLen == 0) {
|
|
strcpy(args,"*.*"); //no arguments.
|
|
} else {
|
|
// handle \, C:\, etc. handle C:, etc.
|
|
if(check_last_split_char(args, argLen, '\\') || args[argLen-1] == ':') {
|
|
strcat(args,"*.*");
|
|
}
|
|
}
|
|
args = ExpandDot(args,buffer,CROSS_LEN,!uselfn);
|
|
|
|
if (DOS_FindDevice(args) != DOS_DEVICES) {
|
|
WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args);
|
|
return;
|
|
}
|
|
if (!strrchr(args,'*') && !strrchr(args,'?')) {
|
|
uint16_t attribute=0;
|
|
if(!DOS_GetSFNPath(args,sargs,false)) {
|
|
if (strlen(args)>1&&toupper(args[0])>='A'&&toupper(args[0])<='Z'&&args[1]==':'&&!Drives[toupper(args[0])-'A'])
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_DRIVE"));
|
|
else
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
|
|
return;
|
|
}
|
|
if(DOS_GetFileAttr(sargs,&attribute) && (attribute&DOS_ATTR_DIRECTORY) ) {
|
|
DOS_FindFirst(sargs,0xffff & ~DOS_ATTR_VOLUME);
|
|
DOS_DTA dta(dos.dta());
|
|
strcpy(args,sargs);
|
|
strcat(args,"\\*.*"); // if no wildcard and a directory, get its files
|
|
}
|
|
}
|
|
if (!DOS_GetSFNPath(args,sargs,false)) {
|
|
if (strlen(args)>1&&toupper(args[0])>='A'&&toupper(args[0])<='Z'&&args[1]==':'&&!Drives[toupper(args[0])-'A'])
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_DRIVE"));
|
|
else
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
|
|
return;
|
|
}
|
|
if (!(uselfn&&!optZ&&strchr(sargs,'*'))&&!strrchr(sargs,'.'))
|
|
strcat(sargs,".*"); // if no extension, get them all
|
|
sprintf(args,"\"%s\"",sargs);
|
|
|
|
/* Make a full path in the args */
|
|
if (!DOS_Canonicalize(args,path)) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
|
|
return;
|
|
}
|
|
*(strrchr_dbcs(path,'\\')+1)=0;
|
|
if (!DOS_GetSFNPath(path,sargs,true)) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
|
|
return;
|
|
}
|
|
if (*(sargs+strlen(sargs)-1) != '\\') strcat(sargs,"\\");
|
|
if (!optB) {
|
|
#if defined(WIN32) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
|
|
if (Network_IsNetworkResource(args)) {
|
|
WriteOut("\n");
|
|
if (optP) p_count+=optW?5:1;
|
|
} else
|
|
#endif
|
|
{
|
|
if (strlen(sargs)>2&&sargs[1]==':') {
|
|
char c[]=" _:";
|
|
c[1]=toupper(sargs[0]);
|
|
CMD_VOL(c[1]>='A'&&c[1]<='Z'?c:empty_string);
|
|
} else
|
|
CMD_VOL(empty_string);
|
|
if (optP) p_count+=optW?15:3;
|
|
}
|
|
}
|
|
|
|
/* Command uses dta so set it to our internal dta */
|
|
RealPt save_dta=dos.dta();
|
|
dos.dta(dos.tables.tempdta);
|
|
DOS_DTA dta(dos.dta());
|
|
dirs.clear();
|
|
dirs.emplace_back(std::string(args));
|
|
inshell=true;
|
|
while (!dirs.empty()) {
|
|
ctrlbrk=false;
|
|
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, optOGN, optOGD, optOGE, optOGS, optOG, optON, optOD, optOE, optOS, reverseSort, rev2Sort)) {dos.dta(save_dta);inshell=false;return;}
|
|
dirs.erase(dirs.begin());
|
|
}
|
|
inshell=false;
|
|
if (!optB) {
|
|
if (optS) {
|
|
WriteOut("\n");
|
|
if (!dirPaused(this, w_size, optP, optW)) {dos.dta(save_dta);return;}
|
|
if (!file_count&&!dir_count)
|
|
WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args);
|
|
else
|
|
WriteOut(MSG_Get("SHELL_CMD_DIR_FILES_LISTED"));
|
|
if (!dirPaused(this, w_size, optP, optW)) {dos.dta(save_dta);return;}
|
|
}
|
|
/* Show the summary of results */
|
|
FormatNumber(byte_count,numformat);
|
|
WriteOut(MSG_Get("SHELL_CMD_DIR_BYTES_USED"),file_count,numformat);
|
|
if (!dirPaused(this, w_size, optP, optW)) {dos.dta(save_dta);return;}
|
|
uint8_t drive=dta.GetSearchDrive();
|
|
uint64_t free_space=1024u*1024u*100u;
|
|
if (Drives[drive]) {
|
|
uint32_t bytes_sector32;uint32_t sectors_cluster32;uint32_t total_clusters32;uint32_t free_clusters32;
|
|
if ((dos.version.major > 7 || (dos.version.major == 7 && dos.version.minor >= 10)) &&
|
|
Drives[drive]->AllocationInfo32(&bytes_sector32,§ors_cluster32,&total_clusters32,&free_clusters32)) { /* FAT32 aware extended API */
|
|
freec=0;
|
|
free_space=(uint64_t)bytes_sector32 * (Bitu)sectors_cluster32 * (Bitu)free_clusters32;
|
|
} else {
|
|
uint16_t bytes_sector;uint8_t sectors_cluster;uint16_t total_clusters;uint16_t free_clusters;
|
|
rsize=true;
|
|
freec=0;
|
|
Drives[drive]->AllocationInfo(&bytes_sector,§ors_cluster,&total_clusters,&free_clusters);
|
|
free_space=(uint64_t)bytes_sector * (Bitu)sectors_cluster * (Bitu)(freec?freec:free_clusters);
|
|
rsize=false;
|
|
}
|
|
}
|
|
#if defined(WIN32) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
|
|
if (Network_IsNetworkResource(args)) {
|
|
std::string str = MSG_Get("SHELL_CMD_DIR_BYTES_FREE");
|
|
std::string::size_type idx = str.rfind(" %");
|
|
if (idx != std::string::npos) {
|
|
str = str.substr(0, idx)+"\n"; // No "nnn Bytes free"
|
|
WriteOut(str.c_str(),dir_count);
|
|
if (!dirPaused(this, w_size, optP, optW)) {dos.dta(save_dta);return;}
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
FormatNumber(free_space,numformat);
|
|
WriteOut(MSG_Get("SHELL_CMD_DIR_BYTES_FREE"),dir_count,numformat);
|
|
if (!dirPaused(this, w_size, optP, optW)) {dos.dta(save_dta);return;}
|
|
}
|
|
}
|
|
dos.dta(save_dta);
|
|
}
|
|
|
|
void DOS_Shell::CMD_LS(char *args) {
|
|
HELP("LS");
|
|
bool optA=ScanCMDBool(args,"A");
|
|
bool optL=ScanCMDBool(args,"L");
|
|
bool optP=ScanCMDBool(args,"P");
|
|
bool optZ=ScanCMDBool(args,"Z");
|
|
char * rem=ScanCMDRemain(args);
|
|
if (rem) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
|
|
return;
|
|
}
|
|
if (!outcon) optL = true;
|
|
|
|
RealPt save_dta=dos.dta();
|
|
dos.dta(dos.tables.tempdta);
|
|
DOS_DTA dta(dos.dta());
|
|
|
|
std::string pattern = args;
|
|
trim(pattern);
|
|
|
|
const char last_char = (pattern.length() > 0 ? pattern.back() : '\0');
|
|
switch (last_char) {
|
|
case '\0': // No arguments, search for all.
|
|
pattern = "*.*";
|
|
break;
|
|
case '\\': // Handle \, C:\, etc.
|
|
case ':': // Handle C:, etc.
|
|
pattern += "*.*";
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
// Handle patterns starting with a dot.
|
|
char buffer[CROSS_LEN];
|
|
pattern = ExpandDot((char *)pattern.c_str(), buffer, sizeof(buffer), true);
|
|
|
|
// When there's no wildcard and target is a directory then search files
|
|
// inside the directory.
|
|
const char *p = pattern.c_str();
|
|
if (!strrchr(p, '*') && !strrchr(p, '?')) {
|
|
uint16_t attr = 0;
|
|
if (DOS_GetFileAttr(p, &attr) && (attr & DOS_ATTR_DIRECTORY))
|
|
pattern += "\\*.*";
|
|
}
|
|
|
|
// If no extension, list all files.
|
|
// This makes patterns like foo* work.
|
|
if (!strrchr(pattern.c_str(), '.'))
|
|
pattern += ".*";
|
|
|
|
char spattern[CROSS_LEN];
|
|
if (!DOS_GetSFNPath(pattern.c_str(),spattern,false)) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
|
|
return;
|
|
}
|
|
int fbak=lfn_filefind_handle;
|
|
lfn_filefind_handle=uselfn?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE;
|
|
bool ret = DOS_FindFirst(((uselfn?"\"":"")+std::string(spattern)+(uselfn?"\"":"")).c_str(), 0xffff & ~DOS_ATTR_VOLUME);
|
|
if (!ret) {
|
|
lfn_filefind_handle=fbak;
|
|
if (strlen(trim(args)))
|
|
WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"), trim(args));
|
|
else
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
|
|
dos.dta(save_dta);
|
|
return;
|
|
}
|
|
|
|
std::vector<DtaResult> results;
|
|
// reserve space for as many as we can fit into a single memory page
|
|
// nothing more to it; make it larger if necessary
|
|
results.reserve(MEM_PAGE_SIZE / sizeof(DtaResult));
|
|
|
|
do {
|
|
DtaResult result;
|
|
dta.GetResult(result.name, result.lname, result.size, result.hsize, result.date, result.time, result.attr);
|
|
results.push_back(result);
|
|
} while ((ret = DOS_FindNext()) == true);
|
|
lfn_filefind_handle=fbak;
|
|
|
|
size_t w_count, p_count, col;
|
|
unsigned int max[15], total, tcols=IS_PC98_ARCH?80:real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
|
|
if (!tcols) tcols=80;
|
|
int mrow=tcols>80?15:10;
|
|
|
|
for (col=mrow; col>0; col--) {
|
|
for (int i=0; i<mrow; i++) max[i]=2;
|
|
if (optL) col=1;
|
|
if (col==1) break;
|
|
w_count=0;
|
|
for (const auto &entry : results) {
|
|
std::string name = uselfn&&!optZ?entry.lname:entry.name;
|
|
if (name == "." || name == "..") continue;
|
|
if (!optA && (entry.attr&DOS_ATTR_SYSTEM || entry.attr&DOS_ATTR_HIDDEN)) continue;
|
|
if (name.size()+2>max[w_count%col]) max[w_count%col]=(unsigned int)(name.size()+2);
|
|
++w_count;
|
|
}
|
|
total=0;
|
|
for (size_t i=0; i<col; i++) total+=max[i];
|
|
if (total<tcols) break;
|
|
}
|
|
ctrlbrk=false;
|
|
w_count = p_count = 0;
|
|
|
|
for (const auto &entry : results) {
|
|
if (CheckBreak(this)) {dos.dta(save_dta);return;}
|
|
std::string name = uselfn&&!optZ?entry.lname:entry.name;
|
|
if (name == "." || name == "..") continue;
|
|
if (!optA && (entry.attr&DOS_ATTR_SYSTEM || entry.attr&DOS_ATTR_HIDDEN)) continue;
|
|
if (entry.attr & DOS_ATTR_DIRECTORY) {
|
|
if (!uselfn||optZ) upcase(name);
|
|
if (col==1) {
|
|
WriteOut("\033[34;1m");
|
|
p_count+=WriteOut_NoParsing(name.c_str(), true);
|
|
WriteOut("\033[0m\n");
|
|
p_count++;
|
|
} else
|
|
WriteOut("\033[34;1m%s\033[0m%-*s", name.c_str(), max[w_count % col]-name.size(), "");
|
|
} else {
|
|
if (!uselfn||optZ) lowcase(name);
|
|
bool is_executable=false;
|
|
if (name.length()>4) {
|
|
std::string ext=name.substr(name.length()-4);
|
|
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
|
|
if (ext==".exe"||ext==".com"||ext==".bat") is_executable=true;
|
|
}
|
|
if (col==1) {
|
|
if (is_executable) WriteOut("\033[32;1m");
|
|
p_count+=WriteOut_NoParsing(name.c_str(), true);
|
|
WriteOut(is_executable?"\033[0m\n":"\n");
|
|
p_count++;
|
|
} else
|
|
WriteOut(is_executable?"\033[32;1m%-*s\033[0m":"%-*s", max[w_count % col], name.c_str());
|
|
}
|
|
if (col>1) {
|
|
++w_count;
|
|
if (w_count % col == 0) {p_count++;WriteOut_NoParsing("\n");}
|
|
}
|
|
if (optP&&p_count>=GetPauseCount()) {
|
|
WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
|
|
uint8_t c;uint16_t n=1;
|
|
DOS_ReadFile(STDIN,&c,&n);
|
|
if (c==3) {dos.dta(save_dta);return;}
|
|
if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
|
|
p_count=0;
|
|
}
|
|
}
|
|
if (col>1&&w_count%col) WriteOut_NoParsing("\n");
|
|
dos.dta(save_dta);
|
|
}
|
|
|
|
struct copysource {
|
|
std::string filename;
|
|
bool concat;
|
|
copysource(std::string& filein,bool concatin):
|
|
filename(filein),concat(concatin){ };
|
|
copysource():filename(""),concat(false){ };
|
|
};
|
|
|
|
void DOS_Shell::CMD_COPY(char * args) {
|
|
HELP("COPY");
|
|
static std::string defaulttarget = ".";
|
|
const char ch_y = MSG_Get("INT21_6523_YESNO_CHARS")[0];
|
|
const char ch_n = MSG_Get("INT21_6523_YESNO_CHARS")[1];
|
|
const char ch_Y = toupper(ch_y);
|
|
const char ch_N = toupper(ch_n);
|
|
const char ch_a = MSG_Get("SHELL_ALLFILES_CHAR")[0];
|
|
const char ch_A = toupper(ch_a);
|
|
StripSpaces(args);
|
|
/* Command uses dta so set it to our internal dta */
|
|
RealPt save_dta=dos.dta();
|
|
dos.dta(dos.tables.tempdta);
|
|
DOS_DTA dta(dos.dta());
|
|
uint32_t size,hsize;uint16_t date;uint16_t time;uint8_t attr;
|
|
char name[DOS_NAMELENGTH_ASCII], lname[LFN_NAMELENGTH+1];
|
|
std::vector<copysource> sources;
|
|
// ignore /b and /t switches: always copy binary
|
|
while(ScanCMDBool(args,"B")) ;
|
|
while(ScanCMDBool(args,"T")) ; //Shouldn't this be A ?
|
|
while(ScanCMDBool(args,"A")) ;
|
|
bool optY=ScanCMDBool(args,"Y");
|
|
if (bf||call||exec) optY=true;
|
|
std::string line;
|
|
if(GetEnvStr("COPYCMD",line)){
|
|
std::string::size_type idx = line.find('=');
|
|
std::string value=line.substr(idx +1 , std::string::npos);
|
|
char copycmd[CROSS_LEN];
|
|
strcpy(copycmd, value.c_str());
|
|
if (ScanCMDBool(copycmd, "Y") && !ScanCMDBool(copycmd, "-Y")) optY = true;
|
|
}
|
|
if (ScanCMDBool(args,"-Y")) optY=false;
|
|
ScanCMDBool(args,"V");
|
|
|
|
char * rem=ScanCMDRemain(args);
|
|
if (rem) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
|
|
dos.dta(save_dta);
|
|
return;
|
|
}
|
|
// Gather all sources (extension to copy more then 1 file specified at command line)
|
|
// Concatenating files go as follows: All parts except for the last bear the concat flag.
|
|
// This construction allows them to be counted (only the non concat set)
|
|
char q[]="\"";
|
|
char* source_p = NULL;
|
|
char source_x[DOS_PATHLENGTH+CROSS_LEN];
|
|
while ( (source_p = StripArg(args)) && *source_p ) {
|
|
do {
|
|
char* plus = strchr(source_p,'+');
|
|
// If StripWord() previously cut at a space before a plus then
|
|
// set concatenate flag on last source and remove leading plus.
|
|
if (plus == source_p && sources.size()) {
|
|
sources[sources.size()-1].concat = true;
|
|
// If spaces also followed plus then item is only a plus.
|
|
if (strlen(++source_p)==0) break;
|
|
plus = strchr(source_p,'+');
|
|
}
|
|
if (plus) {
|
|
char *c=source_p+strlen(source_p)-1;
|
|
if (*source_p=='"'&&*c=='"') {
|
|
*c=0;
|
|
if (strchr(source_p+1,'"'))
|
|
*plus++ = 0;
|
|
else
|
|
plus=NULL;
|
|
*c='"';
|
|
} else
|
|
*plus++ = 0;
|
|
}
|
|
safe_strncpy(source_x,source_p,CROSS_LEN);
|
|
bool has_drive_spec = false;
|
|
size_t source_x_len = strlen(source_x);
|
|
if (source_x_len>0) {
|
|
if (source_x[source_x_len-1]==':') has_drive_spec = true;
|
|
else if (uselfn&&strchr(source_x, '*')) {
|
|
char * find_last;
|
|
find_last=strrchr_dbcs(source_x,'\\');
|
|
if (find_last==NULL) find_last=source_x;
|
|
else find_last++;
|
|
if (strlen(find_last)>0&&source_x[source_x_len-1]=='*'&&strchr(find_last, '.')==NULL) strcat(source_x, ".*");
|
|
}
|
|
}
|
|
if (!has_drive_spec && !strpbrk(source_p,"*?") ) { //doubt that fu*\*.* is valid
|
|
char spath[DOS_PATHLENGTH];
|
|
if (DOS_GetSFNPath(source_p,spath,false)) {
|
|
bool root=false;
|
|
if (strlen(spath)==3&&spath[1]==':'&&spath[2]=='\\') {
|
|
root=true;
|
|
strcat(spath, "*.*");
|
|
}
|
|
if (DOS_FindFirst(spath,0xffff & ~DOS_ATTR_VOLUME)) {
|
|
dta.GetResult(name,lname,size,hsize,date,time,attr);
|
|
if (attr & DOS_ATTR_DIRECTORY || root)
|
|
strcat(source_x,"\\*.*");
|
|
}
|
|
}
|
|
}
|
|
std::string source_xString = std::string(source_x);
|
|
sources.push_back(copysource(source_xString,(plus)?true:false));
|
|
source_p = plus;
|
|
} while(source_p && *source_p);
|
|
}
|
|
// At least one source has to be there
|
|
if (!sources.size() || !sources[0].filename.size()) {
|
|
WriteOut(MSG_Get("SHELL_MISSING_PARAMETER"));
|
|
dos.dta(save_dta);
|
|
return;
|
|
}
|
|
|
|
copysource target;
|
|
// If more then one object exists and last target is not part of a
|
|
// concat sequence then make it the target.
|
|
if(sources.size()>1 && !sources[sources.size()-2].concat){
|
|
target = sources.back();
|
|
sources.pop_back();
|
|
}
|
|
//If no target => default target with concat flag true to detect a+b+c
|
|
if(target.filename.size() == 0) target = copysource(defaulttarget,true);
|
|
|
|
copysource oldsource;
|
|
copysource source;
|
|
uint32_t count = 0;
|
|
while(sources.size()) {
|
|
/* Get next source item and keep track of old source for concat start end */
|
|
oldsource = source;
|
|
source = sources[0];
|
|
sources.erase(sources.begin());
|
|
|
|
//Skip first file if doing a+b+c. Set target to first file
|
|
if(!oldsource.concat && source.concat && target.concat) {
|
|
target = source;
|
|
continue;
|
|
}
|
|
|
|
/* Make a full path in the args */
|
|
char pathSourcePre[DOS_PATHLENGTH], pathSource[DOS_PATHLENGTH+2];
|
|
char pathTarget[DOS_PATHLENGTH];
|
|
|
|
if (!DOS_Canonicalize(const_cast<char*>(source.filename.c_str()),pathSourcePre)) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
|
|
dos.dta(save_dta);
|
|
return;
|
|
}
|
|
strcpy(pathSource,pathSourcePre);
|
|
if (uselfn) sprintf(pathSource,"\"%s\"",pathSourcePre);
|
|
// cut search pattern
|
|
char* pos = strrchr_dbcs(pathSource,'\\');
|
|
if (pos) *(pos+1) = 0;
|
|
|
|
if (!DOS_Canonicalize(const_cast<char*>(target.filename.c_str()),pathTarget)) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
|
|
dos.dta(save_dta);
|
|
return;
|
|
}
|
|
char* temp = strstr(pathTarget,"*.*");
|
|
if(temp && (temp == pathTarget || temp[-1] == '\\')) *temp = 0;//strip off *.* from target
|
|
|
|
// add '\\' if target is a directory
|
|
bool target_is_file = true;
|
|
if (pathTarget[strlen(pathTarget)-1]!='\\') {
|
|
if (DOS_FindFirst(pathTarget,0xffff & ~DOS_ATTR_VOLUME)) {
|
|
dta.GetResult(name,lname,size,hsize,date,time,attr);
|
|
if (attr & DOS_ATTR_DIRECTORY) {
|
|
strcat(pathTarget,"\\");
|
|
target_is_file = false;
|
|
}
|
|
}
|
|
} else target_is_file = false;
|
|
|
|
//Find first sourcefile
|
|
char sPath[DOS_PATHLENGTH];
|
|
bool ret=DOS_GetSFNPath(source.filename.c_str(),sPath,false) && DOS_FindFirst(((strchr(sPath, ' ')&&sPath[0]!='"'?"\"":"")+std::string(sPath)+(strchr(sPath, ' ')&&sPath[strlen(sPath)-1]!='"'?"\"":"")).c_str(),0xffff & ~DOS_ATTR_VOLUME);
|
|
if (!ret) {
|
|
WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),source.filename.c_str());
|
|
dos.dta(save_dta);
|
|
return;
|
|
}
|
|
|
|
uint16_t sourceHandle,targetHandle = 0;
|
|
char nameTarget[DOS_PATHLENGTH];
|
|
char nameSource[DOS_PATHLENGTH], nametmp[DOS_PATHLENGTH+2];
|
|
|
|
// Cache so we don't have to recalculate
|
|
size_t pathTargetLen = strlen(pathTarget);
|
|
|
|
// See if we have to substitute filename or extension
|
|
char *ext = 0;
|
|
size_t replacementOffset = 0;
|
|
if (pathTarget[pathTargetLen-1]!='\\') {
|
|
// only if it's not a directory
|
|
ext = strchr(pathTarget, '.');
|
|
if (ext > pathTarget) { // no possible substitution
|
|
if (ext[-1] == '*') {
|
|
// we substitute extension, save it, hide the name
|
|
ext[-1] = 0;
|
|
assert(ext > pathTarget + 1); // pathTarget is fully qualified
|
|
if (ext[-2] != '\\') {
|
|
// there is something before the asterisk
|
|
// save the offset in the source names
|
|
|
|
replacementOffset = source.filename.find('*');
|
|
size_t lastSlash = std::string::npos;
|
|
bool lead = false;
|
|
for (unsigned int i=0; i<source.filename.size(); i++) {
|
|
if (lead) lead = false;
|
|
else if ((IS_PC98_ARCH && shiftjis_lead_byte(source.filename[i])) || (isDBCSCP() && isKanji1(source.filename[i]))) lead = true;
|
|
else if (source.filename[i]=='\\') lastSlash = i;
|
|
}
|
|
if (std::string::npos == lastSlash)
|
|
lastSlash = 0;
|
|
else
|
|
lastSlash++;
|
|
if (std::string::npos == replacementOffset
|
|
|| replacementOffset < lastSlash) {
|
|
// no asterisk found or in wrong place, error
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
|
|
dos.dta(save_dta);
|
|
return;
|
|
}
|
|
replacementOffset -= lastSlash;
|
|
// WriteOut("[II] replacement offset is %d\n", replacementOffset);
|
|
}
|
|
}
|
|
if (ext[1] == '*') {
|
|
// we substitute name, clear the extension
|
|
*ext = 0;
|
|
} else if (ext[-1]) {
|
|
// we don't substitute anything, clear up
|
|
ext = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool echo=dos.echo, second_file_of_current_source = false;
|
|
ctrlbrk=false;
|
|
while (ret) {
|
|
if (CheckBreak(this)) {
|
|
dos.dta(save_dta);
|
|
DOS_CloseFile(sourceHandle);
|
|
if (targetHandle)
|
|
DOS_CloseFile(targetHandle);
|
|
return;
|
|
}
|
|
dta.GetResult(name,lname,size,hsize,date,time,attr);
|
|
|
|
if ((attr & DOS_ATTR_DIRECTORY)==0) {
|
|
uint16_t ftime,fdate;
|
|
|
|
strcpy(nameSource,pathSource);
|
|
strcat(nameSource,name);
|
|
|
|
// Open Source
|
|
if (DOS_OpenFile(nameSource,0,&sourceHandle)) {
|
|
// record the file date/time
|
|
bool ftdvalid = DOS_GetFileDate(sourceHandle, &ftime, &fdate);
|
|
if (!ftdvalid) LOG_MSG("WARNING: COPY cannot obtain file date/time");
|
|
|
|
// Create Target or open it if in concat mode
|
|
strcpy(nameTarget,q);
|
|
strcat(nameTarget,pathTarget);
|
|
|
|
if (ext) { // substitute parts if necessary
|
|
if (!ext[-1]) { // substitute extension
|
|
strcat(nameTarget, (uselfn?lname:name) + replacementOffset);
|
|
char *p=strchr(nameTarget, '.');
|
|
strcpy(p==NULL?nameTarget+strlen(nameTarget):p, ext);
|
|
}
|
|
if (ext[1] == '*') { // substitute name (so just add the extension)
|
|
strcat(nameTarget, strchr(uselfn?lname:name, '.'));
|
|
}
|
|
}
|
|
|
|
if (nameTarget[strlen(nameTarget)-1]=='\\') strcat(nameTarget,uselfn?lname:name);
|
|
strcat(nameTarget,q);
|
|
|
|
//Special variable to ensure that copy * a_file, where a_file is not a directory concats.
|
|
bool special = second_file_of_current_source && target_is_file && strchr(target.filename.c_str(), '*')==NULL;
|
|
second_file_of_current_source = true;
|
|
if (special) oldsource.concat = true;
|
|
if (*nameSource&&*nameTarget) {
|
|
strcpy(nametmp, nameSource[0]!='\"'&&nameTarget[0]=='\"'?"\"":"");
|
|
strcat(nametmp, nameSource);
|
|
strcat(nametmp, nameSource[strlen(nameSource)-1]!='\"'&&nameTarget[strlen(nameTarget)-1]=='\"'?"\"":"");
|
|
} else
|
|
strcpy(nametmp, nameSource);
|
|
if (!oldsource.concat && (!strcasecmp(nameSource, nameTarget) || !strcasecmp(nametmp, nameTarget)))
|
|
{
|
|
WriteOut(MSG_Get("SHELL_CMD_COPY_NOSELF"));
|
|
dos.dta(save_dta);
|
|
DOS_CloseFile(sourceHandle);
|
|
if (targetHandle)
|
|
DOS_CloseFile(targetHandle);
|
|
return;
|
|
}
|
|
uint16_t fattr;
|
|
bool exist = DOS_GetFileAttr(nameTarget, &fattr);
|
|
if (!(attr & DOS_ATTR_DIRECTORY) && DOS_FindDevice(nameTarget) == DOS_DEVICES) {
|
|
if (exist && !optY && !oldsource.concat) {
|
|
dos.echo=false;
|
|
WriteOut(MSG_Get("SHELL_CMD_COPY_CONFIRM"), nameTarget);
|
|
uint8_t c;
|
|
uint16_t n=1;
|
|
while (true)
|
|
{
|
|
DOS_ReadFile (STDIN,&c,&n);
|
|
if (c==3) {dos.dta(save_dta);DOS_CloseFile(sourceHandle);dos.echo=echo;return;}
|
|
if (c==ch_y||c==ch_Y) {WriteOut("%c\r\n", ch_Y);break;}
|
|
if (c==ch_n||c==ch_N) {WriteOut("%c\r\n", ch_N);break;}
|
|
if (c==ch_a||c==ch_A) {WriteOut("%c\r\n", ch_A);optY=true;break;}
|
|
}
|
|
if (c==ch_n||c==ch_N) {DOS_CloseFile(sourceHandle);ret = DOS_FindNext();continue;}
|
|
}
|
|
if (!exist&&size) {
|
|
int drive=strlen(nameTarget)>1&&(nameTarget[1]==':'||nameTarget[2]==':')?(toupper(nameTarget[nameTarget[0]=='"'?1:0])-'A'):-1;
|
|
if (drive>=0&&Drives[drive]) {
|
|
uint16_t bytes_sector;uint8_t sectors_cluster;uint16_t total_clusters;uint16_t free_clusters;
|
|
rsize=true;
|
|
freec=0;
|
|
Drives[drive]->AllocationInfo(&bytes_sector,§ors_cluster,&total_clusters,&free_clusters);
|
|
rsize=false;
|
|
if ((Bitu)bytes_sector * (Bitu)sectors_cluster * (Bitu)(freec?freec:free_clusters)<size) {
|
|
WriteOut(MSG_Get("SHELL_CMD_COPY_NOSPACE"), uselfn?lname:name);
|
|
DOS_CloseFile(sourceHandle);
|
|
ret = DOS_FindNext();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//Don't create a new file when in concat mode
|
|
if (oldsource.concat || DOS_CreateFile(nameTarget,0,&targetHandle)) {
|
|
uint32_t dummy=0;
|
|
|
|
if (DOS_FindDevice(name) == DOS_DEVICES && !DOS_SetFileDate(targetHandle, ftime, fdate))
|
|
LOG_MSG("WARNING: COPY unable to apply date/time to dest");
|
|
|
|
//In concat mode. Open the target and seek to the eof
|
|
if (!oldsource.concat || (DOS_OpenFile(nameTarget,OPEN_READWRITE,&targetHandle) &&
|
|
DOS_SeekFile(targetHandle,&dummy,DOS_SEEK_END))) {
|
|
// Copy
|
|
static uint8_t buffer[0x8000]; // static, otherwise stack overflow possible.
|
|
bool failed = false;
|
|
uint16_t toread = 0x8000;
|
|
bool iscon=DOS_FindDevice(name)==DOS_FindDevice("con");
|
|
if (iscon) dos.echo=true;
|
|
bool cont;
|
|
do {
|
|
if (!DOS_ReadFile(sourceHandle,buffer,&toread)) failed=true;
|
|
if (iscon) {
|
|
if (dos.errorcode==77) {
|
|
dos.dta(save_dta);
|
|
DOS_CloseFile(sourceHandle);
|
|
DOS_CloseFile(targetHandle);
|
|
if (!exist) DOS_UnlinkFile(nameTarget);
|
|
dos.echo=echo;
|
|
return;
|
|
}
|
|
cont=true;
|
|
for (int i=0;i<toread;i++)
|
|
if (buffer[i]==26) {
|
|
toread=i;
|
|
cont=false;
|
|
break;
|
|
}
|
|
if (!DOS_WriteFile(targetHandle,buffer,&toread)) failed=true;
|
|
if (cont) toread=0x8000;
|
|
} else {
|
|
if (DOS_FindDevice(nameTarget)==DOS_FindDevice("con")&&CheckBreak(this)) {failed=true;break;}
|
|
if (!DOS_WriteFile(targetHandle,buffer,&toread)) failed=true;
|
|
cont=toread == 0x8000;
|
|
}
|
|
} while (cont);
|
|
if (!DOS_CloseFile(sourceHandle)) failed=true;
|
|
#if defined(WIN32)
|
|
if (file_access_tries>0 && DOS_FindDevice(name) == DOS_DEVICES) DOS_SetFileDate(targetHandle, ftime, fdate);
|
|
#endif
|
|
if (!DOS_CloseFile(targetHandle)) failed=true;
|
|
if (failed)
|
|
WriteOut(MSG_Get("SHELL_CMD_COPY_ERROR"),uselfn?lname:name);
|
|
else if (strcmp(name,lname)&&uselfn)
|
|
WriteOut(" %s [%s]\n",lname,name);
|
|
else
|
|
WriteOut(" %s\n",uselfn?lname:name);
|
|
if(!source.concat && !special && !failed) count++; //Only count concat files once
|
|
} else {
|
|
DOS_CloseFile(sourceHandle);
|
|
WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast<char*>(target.filename.c_str()));
|
|
}
|
|
} else {
|
|
DOS_CloseFile(sourceHandle);
|
|
WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast<char*>(target.filename.c_str()));
|
|
}
|
|
} else WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast<char*>(source.filename.c_str()));
|
|
}
|
|
//On to the next file if the previous one wasn't a device
|
|
if ((attr&DOS_ATTR_DEVICE) == 0) ret = DOS_FindNext();
|
|
else ret = false;
|
|
}
|
|
}
|
|
|
|
WriteOut(MSG_Get("SHELL_CMD_COPY_SUCCESS"),count);
|
|
dos.dta(save_dta);
|
|
dos.echo=echo;
|
|
Drives[DOS_GetDefaultDrive()]->EmptyCache();
|
|
}
|
|
|
|
|
|
/* NTS: WARNING, this function modifies the buffer pointed to by char *args */
|
|
void DOS_Shell::CMD_SET(char * args) {
|
|
HELP("SET");
|
|
StripSpaces(args);
|
|
|
|
std::string line;
|
|
if (*args == 0) { /* "SET" by itself means to show the environment block */
|
|
Bitu count = GetEnvCount();
|
|
|
|
for (Bitu a = 0;a < count;a++) {
|
|
if (GetEnvNum(a,line))
|
|
WriteOut("%s\n",line.c_str());
|
|
}
|
|
|
|
}
|
|
else {
|
|
char *p;
|
|
|
|
{ /* parse arguments at the start */
|
|
char *pcheck = args;
|
|
|
|
while (*pcheck != 0 && (*pcheck == ' ' || *pcheck == '\t')) pcheck++;
|
|
if (*pcheck != 0 && strlen(pcheck) > 3 && (strncasecmp(pcheck,"/p ",3) == 0))
|
|
E_Exit("Set /P is not supported. Use Choice!"); /* TODO: What is SET /P supposed to do? */
|
|
}
|
|
|
|
/* Most SET commands take the form NAME=VALUE */
|
|
p = strchr(args,'=');
|
|
if (p == NULL) {
|
|
/* SET <variable> without assignment prints the variable instead */
|
|
if (!GetEnvStr(args,line)) WriteOut(MSG_Get("SHELL_CMD_SET_NOT_SET"),args);
|
|
WriteOut("%s\n",line.c_str());
|
|
} else {
|
|
/* ASCIIZ snip the args string in two, so that args is C-string name of the variable,
|
|
* and "p" is C-string value of the variable */
|
|
*p++ = 0;
|
|
std::string vstr = p;
|
|
bool zdirpath = static_cast<Section_prop *>(control->GetSection("dos"))->Get_bool("drive z expand path");
|
|
if (zdirpath && !strcasecmp(args, "path")) GetExpandedPath(vstr);
|
|
/* No parsing is needed. The command interpreter does the variable substitution for us */
|
|
if (!SetEnv(args,vstr.c_str())) {
|
|
/* NTS: If Win95 is any example, the command interpreter expands the variables for us */
|
|
WriteOut(MSG_Get("SHELL_CMD_SET_OUT_OF_SPACE"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DOS_Shell::CMD_IF(char * args) {
|
|
HELP("IF");
|
|
StripSpaces(args,'=');
|
|
bool has_not=false;
|
|
|
|
while (strncasecmp(args,"NOT",3) == 0) {
|
|
if (!isspace(*reinterpret_cast<unsigned char*>(&args[3])) && (args[3] != '=')) break;
|
|
args += 3; //skip text
|
|
//skip more spaces
|
|
StripSpaces(args,'=');
|
|
has_not = !has_not;
|
|
}
|
|
|
|
if(strncasecmp(args,"ERRORLEVEL",10) == 0) {
|
|
args += 10; //skip text
|
|
//Strip spaces and ==
|
|
StripSpaces(args,'=');
|
|
char* word = StripWord(args);
|
|
if(!isdigit(*word)) {
|
|
WriteOut(MSG_Get("SHELL_CMD_IF_ERRORLEVEL_MISSING_NUMBER"));
|
|
return;
|
|
}
|
|
|
|
uint8_t n = 0;
|
|
do n = n * 10 + (*word - '0');
|
|
while (isdigit(*++word));
|
|
if(*word && !isspace(*word)) {
|
|
WriteOut(MSG_Get("SHELL_CMD_IF_ERRORLEVEL_INVALID_NUMBER"));
|
|
return;
|
|
}
|
|
/* Read the error code from DOS */
|
|
if ((dos.return_code>=n) ==(!has_not)) DoCommand(args);
|
|
return;
|
|
}
|
|
|
|
if(strncasecmp(args,"EXIST ",6) == 0) {
|
|
args += 6; //Skip text
|
|
StripSpaces(args);
|
|
char* word = StripArg(args);
|
|
if (!*word) {
|
|
WriteOut(MSG_Get("SHELL_CMD_IF_EXIST_MISSING_FILENAME"));
|
|
return;
|
|
}
|
|
|
|
{ /* DOS_FindFirst uses dta so set it to our internal dta */
|
|
char spath[DOS_PATHLENGTH], path[DOS_PATHLENGTH], pattern[DOS_PATHLENGTH], full[DOS_PATHLENGTH], *r;
|
|
if (!DOS_Canonicalize(word,full)) {
|
|
if (has_not) DoCommand(args);
|
|
return;
|
|
}
|
|
r=strrchr_dbcs(full, '\\');
|
|
if (r!=NULL) {
|
|
*r=0;
|
|
strcpy(path, full);
|
|
strcat(path, "\\");
|
|
strcpy(pattern, r+1);
|
|
*r='\\';
|
|
} else {
|
|
strcpy(path, "");
|
|
strcpy(pattern, full);
|
|
}
|
|
int k=0;
|
|
for (int i=0;i<(int)strlen(pattern);i++)
|
|
if (pattern[i]!='\"')
|
|
pattern[k++]=pattern[i];
|
|
pattern[k]=0;
|
|
strcpy(spath, path);
|
|
if (strchr_dbcs(word,'\"')||uselfn) {
|
|
if (!DOS_GetSFNPath(("\""+std::string(path)+"\\").c_str(), spath, false)) strcpy(spath, path);
|
|
if (!strlen(spath)||spath[strlen(spath)-1]!='\\') strcat(spath, "\\");
|
|
}
|
|
RealPt save_dta=dos.dta();
|
|
dos.dta(dos.tables.tempdta);
|
|
int fbak=lfn_filefind_handle;
|
|
lfn_filefind_handle=uselfn?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE;
|
|
std::string sfull=std::string(spath)+std::string(pattern);
|
|
bool ret=DOS_FindFirst(((uselfn&&sfull.length()&&sfull[0]!='"'?"\"":"")+sfull+(uselfn&&sfull.length()&&sfull[sfull.length()-1]!='"'?"\"":"")).c_str(),0xffff & ~(DOS_ATTR_VOLUME|DOS_ATTR_DIRECTORY));
|
|
lfn_filefind_handle=fbak;
|
|
dos.dta(save_dta);
|
|
if (ret==(!has_not)) DoCommand(args);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Normal if string compare */
|
|
|
|
char* word1 = args;
|
|
// first word is until space or =
|
|
while (*args && !isspace(*reinterpret_cast<unsigned char*>(args)) && (*args != '='))
|
|
args++;
|
|
char* end_word1 = args;
|
|
|
|
// scan for =
|
|
while (*args && (*args != '='))
|
|
args++;
|
|
// check for ==
|
|
if ((*args==0) || (args[1] != '=')) {
|
|
SyntaxError();
|
|
return;
|
|
}
|
|
args += 2;
|
|
StripSpaces(args,'=');
|
|
|
|
char* word2 = args;
|
|
// second word is until space or =
|
|
while (*args && !isspace(*reinterpret_cast<unsigned char*>(args)) && (*args != '='))
|
|
args++;
|
|
|
|
if (*args) {
|
|
*end_word1 = 0; // mark end of first word
|
|
*args++ = 0; // mark end of second word
|
|
StripSpaces(args,'=');
|
|
|
|
if ((strcmp(word1,word2)==0)==(!has_not)) DoCommand(args);
|
|
}
|
|
}
|
|
|
|
void DOS_Shell::CMD_GOTO(char * args) {
|
|
HELP("GOTO");
|
|
StripSpaces(args);
|
|
if (!bf) return;
|
|
if (*args==':') args++;
|
|
//label ends at the first space
|
|
char* non_space = args;
|
|
while (*non_space) {
|
|
if((*non_space == ' ') || (*non_space == '\t'))
|
|
*non_space = 0;
|
|
else non_space++;
|
|
}
|
|
if (!*args) {
|
|
WriteOut(MSG_Get("SHELL_CMD_GOTO_MISSING_LABEL"));
|
|
return;
|
|
}
|
|
if (!bf->Goto(args)) {
|
|
WriteOut(MSG_Get("SHELL_CMD_GOTO_LABEL_NOT_FOUND"),args);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void DOS_Shell::CMD_SHIFT(char * args ) {
|
|
HELP("SHIFT");
|
|
if(bf) bf->Shift();
|
|
}
|
|
|
|
void DOS_Shell::CMD_TYPE(char * args) {
|
|
HELP("TYPE");
|
|
|
|
// ignore /p /h and /t for compatibility
|
|
ScanCMDBool(args,"P");
|
|
ScanCMDBool(args,"H");
|
|
ScanCMDBool(args,"T");
|
|
StripSpaces(args);
|
|
if (strcasecmp(args,"nul")==0) return;
|
|
if (!*args) {
|
|
WriteOut(MSG_Get("SHELL_SYNTAXERROR"));
|
|
return;
|
|
}
|
|
uint16_t handle;
|
|
char * word;
|
|
bool lead = false;
|
|
int COLS = 80;
|
|
if (!IS_PC98_ARCH && outcon) COLS=real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
|
|
BIOS_NCOLS;
|
|
uint8_t page=outcon?real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE):0;
|
|
nextfile:
|
|
word=StripArg(args);
|
|
if (!DOS_OpenFile(word,0,&handle)) {
|
|
WriteOut(MSG_Get(dos.errorcode==DOSERR_ACCESS_DENIED?"SHELL_CMD_FILE_ACCESS_DENIED":(dos.errorcode==DOSERR_PATH_NOT_FOUND?"SHELL_ILLEGAL_PATH":"SHELL_CMD_FILE_NOT_FOUND")),word);
|
|
return;
|
|
}
|
|
ctrlbrk=false;
|
|
uint8_t c,last,last2,last3;uint16_t n=1;
|
|
last3=last2=last=0;
|
|
bool iscon=DOS_FindDevice(word)==DOS_FindDevice("con");
|
|
while (n) {
|
|
DOS_ReadFile(handle,&c,&n);
|
|
if (outcon && !CURSOR_POS_COL(page)) last3=last2=last=0;
|
|
if (lead) lead=false;
|
|
else if ((IS_PC98_ARCH || isDBCSCP())
|
|
#if defined(USE_TTF)
|
|
&& dbcs_sbcs
|
|
#endif
|
|
) lead = isKanji1(c) && !(TTF_using()
|
|
#if defined(USE_TTF)
|
|
&& autoboxdraw
|
|
#endif
|
|
&& CheckBoxDrawing(last3, last2, last, c));
|
|
if (n==0 || c==0x1a) break; // stop at EOF
|
|
if (iscon) {
|
|
if (c==3) break;
|
|
else if (c==13) WriteOut("\r\n");
|
|
} else if (CheckBreak(this)) break;
|
|
else if (outcon && lead && CURSOR_POS_COL(page) == COLS-1) WriteOut(" ");
|
|
DOS_WriteFile(STDOUT,&c,&n);
|
|
if (outcon) {last3=last2;last2=last;last=c;}
|
|
}
|
|
DOS_CloseFile(handle);
|
|
if (*args) goto nextfile;
|
|
}
|
|
|
|
void DOS_Shell::CMD_REM(char * args) {
|
|
HELP("REM");
|
|
}
|
|
|
|
static char PAUSED(void) {
|
|
uint8_t c; uint16_t n=1, handle;
|
|
if (!usecon&&DOS_OpenFile("con", OPEN_READWRITE, &handle)) {
|
|
DOS_ReadFile (handle,&c,&n);
|
|
DOS_CloseFile(handle);
|
|
} else
|
|
DOS_ReadFile (STDIN,&c,&n);
|
|
return c;
|
|
}
|
|
|
|
void DOS_Shell::CMD_MORE(char * args) {
|
|
HELP("MORE");
|
|
//ScanCMDBool(args,">");
|
|
int nchars = 0, nlines = 0, linecount = 0, LINES = 25, COLS = 80, TABSIZE = 8;
|
|
char * word;
|
|
uint8_t c,last,last2,last3;
|
|
last3=last2=last=0;
|
|
bool lead=false;
|
|
uint16_t n=1;
|
|
StripSpaces(args);
|
|
if (IS_PC98_ARCH) {
|
|
LINES=real_readb(0x60,0x113) & 0x01 ? 25 : 20;
|
|
COLS=80;
|
|
if (real_readb(0x60,0x111)) LINES--; // Function keys displayed
|
|
} else {
|
|
LINES=(IS_EGAVGA_ARCH?real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS):24)+1;
|
|
COLS=real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
|
|
}
|
|
LINES--;
|
|
if(!*args||!strcasecmp(args, "con")) {
|
|
while (true) {
|
|
DOS_ReadFile (STDIN,&c,&n);
|
|
if (lead) lead=false;
|
|
else if ((IS_PC98_ARCH || isDBCSCP())
|
|
#if defined(USE_TTF)
|
|
&& dbcs_sbcs
|
|
#endif
|
|
) lead = isKanji1(c) && !(TTF_using()
|
|
#if defined(USE_TTF)
|
|
&& autoboxdraw
|
|
#endif
|
|
&& CheckBoxDrawing(last3, last2, last, c));
|
|
if (c==3) {dos.echo=echo;return;}
|
|
else if (n==0) {if (last!=10) WriteOut("\r\n");dos.echo=echo;return;}
|
|
else if (c==13&&last==26) {dos.echo=echo;return;}
|
|
else {
|
|
if (c==10);
|
|
else if (c==13) {
|
|
linecount++;
|
|
WriteOut("\r\n");
|
|
last3=last2=last=0;
|
|
} else if (c=='\t') {
|
|
do {
|
|
WriteOut(" ");
|
|
nchars++;
|
|
} while ( nchars < COLS && nchars % TABSIZE );
|
|
} else {
|
|
if (lead && nchars == COLS-1) {
|
|
last3=last2=last=0;
|
|
nlines++;
|
|
nchars = 0;
|
|
WriteOut("\n");
|
|
if (nlines == LINES) {
|
|
WriteOut("-- More -- (%u) --",linecount);
|
|
if (PAUSED()==3) return;
|
|
WriteOut("\n");
|
|
nlines=0;
|
|
}
|
|
}
|
|
nchars++;
|
|
WriteOut("%c", c);
|
|
}
|
|
if (c == 13 || nchars >= COLS) {
|
|
nlines++;
|
|
nchars = 0;
|
|
if (nlines == LINES) {
|
|
WriteOut("-- More -- (%u) --",linecount);
|
|
if (PAUSED()==3) return;
|
|
WriteOut("\n");
|
|
nlines=0;
|
|
}
|
|
}
|
|
last3=last2;last2=last;last=c;
|
|
}
|
|
}
|
|
}
|
|
if (strcasecmp(args,"nul")==0) return;
|
|
if (!*args) {
|
|
WriteOut(MSG_Get("SHELL_SYNTAXERROR"));
|
|
return;
|
|
}
|
|
uint16_t handle;
|
|
nextfile:
|
|
word=StripArg(args);
|
|
if (!DOS_OpenFile(word,0,&handle)) {
|
|
WriteOut(MSG_Get(dos.errorcode==DOSERR_ACCESS_DENIED?"SHELL_CMD_FILE_ACCESS_DENIED":(dos.errorcode==DOSERR_PATH_NOT_FOUND?"SHELL_ILLEGAL_PATH":"SHELL_CMD_FILE_NOT_FOUND")),word);
|
|
return;
|
|
}
|
|
ctrlbrk=false;
|
|
lead=false;
|
|
last3=last2=last=0;
|
|
nlines=0;
|
|
do {
|
|
n=1;
|
|
DOS_ReadFile(handle,&c,&n);
|
|
if (lead) lead=false;
|
|
else if ((IS_PC98_ARCH || isDBCSCP())
|
|
#if defined(USE_TTF)
|
|
&& dbcs_sbcs
|
|
#endif
|
|
) lead = isKanji1(c) && !(TTF_using()
|
|
#if defined(USE_TTF)
|
|
&& autoboxdraw
|
|
#endif
|
|
&& CheckBoxDrawing(last3, last2, last, c));
|
|
if (lead && nchars == COLS-1) {
|
|
last3=last2=last=0;
|
|
nlines++;
|
|
nchars = 0;
|
|
WriteOut("\n");
|
|
if (nlines == LINES && usecon) {
|
|
WriteOut("-- More -- %s (%u) --",word,linecount);
|
|
if (PAUSED()==3) {DOS_CloseFile(handle);return;}
|
|
WriteOut("\n");
|
|
nlines=0;
|
|
}
|
|
}
|
|
DOS_WriteFile(STDOUT,&c,&n);
|
|
last3=last2;last2=last;last=c;
|
|
if (c != '\t') nchars++;
|
|
else do {
|
|
WriteOut(" ");
|
|
nchars++;
|
|
} while ( nchars < COLS && nchars % TABSIZE );
|
|
|
|
if (c == '\n') linecount++;
|
|
if (c == '\n' || nchars >= COLS) {
|
|
last3=last2=last=0;
|
|
nlines++;
|
|
nchars = 0;
|
|
if (!usecon) {
|
|
if (c != '\n') WriteOut("\n");
|
|
} else if (nlines == LINES) {
|
|
WriteOut("-- More -- %s (%u) --",word,linecount);
|
|
if (PAUSED()==3) {DOS_CloseFile(handle);return;}
|
|
WriteOut("\n");
|
|
nlines=0;
|
|
}
|
|
}
|
|
if (CheckBreak(this)) break;
|
|
} while (n);
|
|
DOS_CloseFile(handle);
|
|
if (*args) {
|
|
WriteOut("\n");
|
|
if (usecon && PAUSED()==3) return;
|
|
goto nextfile;
|
|
}
|
|
}
|
|
|
|
void DOS_Shell::CMD_PAUSE(char * args){
|
|
HELP("PAUSE");
|
|
if(args && *args) {
|
|
args++;
|
|
WriteOut("%s\n",args); // optional specified message
|
|
} else
|
|
WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
|
|
uint8_t c;uint16_t n=1;
|
|
DOS_ReadFile(STDIN,&c,&n);
|
|
if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
|
|
}
|
|
|
|
void DOS_Shell::CMD_CALL(char * args){
|
|
HELP("CALL");
|
|
this->call=true; /* else the old batchfile will be closed first */
|
|
this->ParseLine(args);
|
|
this->call=false;
|
|
}
|
|
|
|
void DOS_Shell::CMD_DATE(char * args) {
|
|
HELP("DATE");
|
|
if(ScanCMDBool(args,"H")) {
|
|
// synchronize date with host parameter
|
|
time_t curtime;
|
|
struct tm *loctime;
|
|
curtime = time (NULL);
|
|
loctime = localtime (&curtime);
|
|
|
|
reg_cx = loctime->tm_year+1900;
|
|
reg_dh = loctime->tm_mon+1;
|
|
reg_dl = loctime->tm_mday;
|
|
|
|
reg_ah=0x2b; // set system date
|
|
CALLBACK_RunRealInt(0x21);
|
|
if (sync_time) {manualtime=false;mainMenu.get_item("sync_host_datetime").check(true).refresh_item(mainMenu);}
|
|
return;
|
|
} else if(ScanCMDBool(args,"S")) {
|
|
sync_time=true;
|
|
manualtime=false;
|
|
mainMenu.get_item("sync_host_datetime").check(true).refresh_item(mainMenu);
|
|
return;
|
|
} else if(ScanCMDBool(args,"F")) {
|
|
sync_time=false;
|
|
manualtime=false;
|
|
mainMenu.get_item("sync_host_datetime").check(false).refresh_item(mainMenu);
|
|
return;
|
|
}
|
|
// check if a date was passed in command line
|
|
char c=dos.tables.country[11], c1, c2;
|
|
uint32_t newday,newmonth,newyear;
|
|
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));
|
|
if (n==5 && c1==c && c2==c) {
|
|
reg_cx = static_cast<uint16_t>(newyear);
|
|
reg_dh = static_cast<uint8_t>(newmonth);
|
|
reg_dl = static_cast<uint8_t>(newday);
|
|
|
|
reg_ah=0x2b; // set system date
|
|
CALLBACK_RunRealInt(0x21);
|
|
if(reg_al==0xff) WriteOut(MSG_Get("SHELL_CMD_DATE_ERROR"));
|
|
return;
|
|
}
|
|
// display the current date
|
|
reg_ah=0x2a; // get system date
|
|
CALLBACK_RunRealInt(0x21);
|
|
|
|
const char* datestring = MSG_Get("SHELL_CMD_DATE_DAYS");
|
|
uint32_t length;
|
|
char day[6] = {0};
|
|
if(sscanf(datestring,"%u",&length) && (length<7) && (strlen(datestring)==((size_t)length*7+1))) {
|
|
// date string appears valid
|
|
for(uint32_t i = 0; i < length; i++) day[i] = datestring[reg_al*length+1+i];
|
|
}
|
|
bool dateonly = ScanCMDBool(args,"T");
|
|
if(!dateonly) WriteOut(MSG_Get("SHELL_CMD_DATE_NOW"));
|
|
if (!dateonly) WriteOut("%s ", day);
|
|
WriteOut("%s\n",FormatDate((uint16_t)reg_cx, (uint8_t)reg_dh, (uint8_t)reg_dl));
|
|
if(!dateonly) {
|
|
char format[11];
|
|
sprintf(format, dos.tables.country[0]==1?"DD%cMM%cYYYY":(dos.tables.country[0]==2?"YYYY%cMM%cDD":"MM%cDD%cYYYY"), c, c);
|
|
WriteOut(MSG_Get("SHELL_CMD_DATE_SETHLP"), format);
|
|
}
|
|
}
|
|
|
|
void DOS_Shell::CMD_TIME(char * args) {
|
|
HELP("TIME");
|
|
if(ScanCMDBool(args,"H")) {
|
|
// synchronize time with host parameter
|
|
time_t curtime;
|
|
struct tm *loctime;
|
|
curtime = time (NULL);
|
|
loctime = localtime (&curtime);
|
|
|
|
//reg_cx = loctime->;
|
|
//reg_dh = loctime->;
|
|
//reg_dl = loctime->;
|
|
|
|
// reg_ah=0x2d; // set system time TODO
|
|
// CALLBACK_RunRealInt(0x21);
|
|
|
|
uint32_t ticks=(uint32_t)(((double)(loctime->tm_hour*3600+
|
|
loctime->tm_min*60+
|
|
loctime->tm_sec))*18.206481481);
|
|
mem_writed(BIOS_TIMER,ticks);
|
|
if (sync_time) {manualtime=false;mainMenu.get_item("sync_host_datetime").check(true).refresh_item(mainMenu);}
|
|
return;
|
|
}
|
|
uint32_t newhour,newminute,newsecond;
|
|
char c=dos.tables.country[13], c1, c2;
|
|
if (sscanf(args,"%u%c%u%c%u",&newhour,&c1,&newminute,&c2,&newsecond)==5 && c1==c && c2==c) {
|
|
reg_ch = static_cast<uint16_t>(newhour);
|
|
reg_cl = static_cast<uint8_t>(newminute);
|
|
reg_dx = static_cast<uint8_t>(newsecond)<<8;
|
|
|
|
reg_ah=0x2d; // set system time
|
|
CALLBACK_RunRealInt(0x21);
|
|
if(reg_al==0xff) WriteOut(MSG_Get("SHELL_CMD_TIME_ERROR"));
|
|
return;
|
|
}
|
|
bool timeonly = ScanCMDBool(args,"T");
|
|
|
|
reg_ah=0x2c; // get system time
|
|
CALLBACK_RunRealInt(0x21);
|
|
/*
|
|
reg_dl= // 1/100 seconds
|
|
reg_dh= // seconds
|
|
reg_cl= // minutes
|
|
reg_ch= // hours
|
|
*/
|
|
if(timeonly) {
|
|
WriteOut("%u%c%02u%c%02u\n",reg_ch,dos.tables.country[13],reg_cl,dos.tables.country[13],reg_dh);
|
|
} else {
|
|
WriteOut(MSG_Get("SHELL_CMD_TIME_NOW"));
|
|
WriteOut("%s\n", FormatTime(reg_ch,reg_cl,reg_dh,reg_dl));
|
|
char format[9];
|
|
sprintf(format, "hh%cmm%css", dos.tables.country[13], dos.tables.country[13]);
|
|
WriteOut(MSG_Get("SHELL_CMD_TIME_SETHLP"), format);
|
|
}
|
|
}
|
|
|
|
void DOS_Shell::CMD_SUBST(char * args) {
|
|
/* If more that one type can be substed think of something else
|
|
* E.g. make basedir member dos_drive instead of localdrive
|
|
*/
|
|
HELP("SUBST");
|
|
try {
|
|
char mountstring[DOS_PATHLENGTH+CROSS_LEN+20];
|
|
strcpy(mountstring,"MOUNT ");
|
|
StripSpaces(args);
|
|
std::string arg;
|
|
CommandLine command(0,args);
|
|
if (!command.GetCount()) {
|
|
char name[DOS_NAMELENGTH_ASCII],lname[LFN_NAMELENGTH];
|
|
uint32_t size,hsize;uint16_t date;uint16_t time;uint8_t attr;
|
|
/* Command uses dta so set it to our internal dta */
|
|
RealPt save_dta = dos.dta();
|
|
dos.dta(dos.tables.tempdta);
|
|
DOS_DTA dta(dos.dta());
|
|
|
|
WriteOut(MSG_Get("SHELL_CMD_SUBST_DRIVE_LIST"));
|
|
WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_FORMAT"),"Drive","Type","Label");
|
|
int cols=IS_PC98_ARCH?80:real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
|
|
if (!cols) cols=80;
|
|
for(int p = 0;p < cols;p++) WriteOut("-");
|
|
bool none=true;
|
|
for (int d = 0;d < DOS_DRIVES;d++) {
|
|
if (!Drives[d]||strncmp(Drives[d]->GetInfo(),"local ",6)) continue;
|
|
|
|
char root[7] = {(char)('A'+d),':','\\','*','.','*',0};
|
|
bool ret = DOS_FindFirst(root,DOS_ATTR_VOLUME);
|
|
if (ret) {
|
|
dta.GetResult(name,lname,size,hsize,date,time,attr);
|
|
DOS_FindNext(); //Mark entry as invalid
|
|
} else name[0] = 0;
|
|
|
|
/* Change 8.3 to 11.0 */
|
|
const char* dot = strchr(name, '.');
|
|
if(dot && (dot - name == 8) ) {
|
|
name[8] = name[9];name[9] = name[10];name[10] = name[11];name[11] = 0;
|
|
}
|
|
|
|
root[1] = 0; //This way, the format string can be reused.
|
|
WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_FORMAT"),root, Drives[d]->GetInfo(),name);
|
|
none=false;
|
|
}
|
|
if (none) WriteOut(MSG_Get("PROGRAM_IMGMOUNT_STATUS_NONE"));
|
|
dos.dta(save_dta);
|
|
return;
|
|
}
|
|
|
|
if (command.GetCount() != 2) throw 0 ;
|
|
|
|
command.FindCommand(1,arg);
|
|
if( (arg.size()>1) && arg[1] !=':') throw(0);
|
|
char temp_str[2] = { 0,0 };
|
|
temp_str[0]=(char)toupper(args[0]);
|
|
command.FindCommand(2,arg);
|
|
if((arg=="/D") || (arg=="/d")) {
|
|
if(!Drives[temp_str[0]-'A'] ) throw 1; //targetdrive not in use
|
|
strcat(mountstring,"-u ");
|
|
strcat(mountstring,temp_str);
|
|
this->ParseLine(mountstring);
|
|
return;
|
|
}
|
|
if(Drives[temp_str[0]-'A'] ) throw 2; //targetdrive in use
|
|
strcat(mountstring,temp_str);
|
|
strcat(mountstring," ");
|
|
|
|
uint8_t drive;char dir[DOS_PATHLENGTH+2],fulldir[DOS_PATHLENGTH];
|
|
if (strchr(arg.c_str(),'\"')==NULL)
|
|
sprintf(dir,"\"%s\"",arg.c_str());
|
|
else strcpy(dir,arg.c_str());
|
|
if (!DOS_MakeName(dir,fulldir,&drive)) throw 3;
|
|
|
|
localDrive* ldp=0;
|
|
if( ( ldp=dynamic_cast<localDrive*>(Drives[drive])) == 0 ) throw 4;
|
|
char newname[CROSS_LEN];
|
|
strcpy(newname, ldp->basedir);
|
|
strcat(newname,fulldir);
|
|
CROSS_FILENAME(newname);
|
|
ldp->dirCache.ExpandName(newname);
|
|
strcat(mountstring,"\"");
|
|
strcat(mountstring, newname);
|
|
strcat(mountstring,"\"");
|
|
this->ParseLine(mountstring);
|
|
}
|
|
catch(int a){
|
|
switch (a) {
|
|
case 1:
|
|
WriteOut(MSG_Get("SHELL_CMD_SUBST_NO_REMOVE"));
|
|
break;
|
|
case 2:
|
|
WriteOut(MSG_Get("SHELL_CMD_SUBST_IN_USE"));
|
|
break;
|
|
case 3:
|
|
WriteOut(MSG_Get("SHELL_CMD_SUBST_INVALID_PATH"));
|
|
break;
|
|
case 4:
|
|
WriteOut(MSG_Get("SHELL_CMD_SUBST_NOT_LOCAL"));
|
|
break;
|
|
default:
|
|
WriteOut(MSG_Get("SHELL_CMD_SUBST_FAILURE"));
|
|
}
|
|
return;
|
|
}
|
|
catch(...) { //dynamic cast failed =>so no localdrive
|
|
WriteOut(MSG_Get("SHELL_CMD_SUBST_FAILURE"));
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void DOS_Shell::CMD_LOADHIGH(char *args){
|
|
HELP("LOADHIGH");
|
|
uint16_t umb_start=dos_infoblock.GetStartOfUMBChain();
|
|
uint8_t umb_flag=dos_infoblock.GetUMBChainState();
|
|
uint8_t old_memstrat=(uint8_t)(DOS_GetMemAllocStrategy()&0xff);
|
|
if (umb_start==0x9fff) {
|
|
if ((umb_flag&1)==0) DOS_LinkUMBsToMemChain(1);
|
|
DOS_SetMemAllocStrategy(0x80); // search in UMBs first
|
|
this->ParseLine(args);
|
|
uint8_t current_umb_flag=dos_infoblock.GetUMBChainState();
|
|
if ((current_umb_flag&1)!=(umb_flag&1)) DOS_LinkUMBsToMemChain(umb_flag);
|
|
DOS_SetMemAllocStrategy(old_memstrat); // restore strategy
|
|
} else this->ParseLine(args);
|
|
}
|
|
|
|
bool get_param(char *&args, char *&rem, char *&temp, char &wait_char, int &wait_sec)
|
|
{
|
|
const char *last = strchr(args, 0);
|
|
StripSpaces(args);
|
|
temp = ScanCMDRemain(args);
|
|
const bool optC = temp && tolower(temp[1]) == 'c';
|
|
const bool optT = temp && tolower(temp[1]) == 't';
|
|
if (temp && *temp && !optC && !optT)
|
|
return false;
|
|
if (temp) {
|
|
if (args == temp)
|
|
args = strchr(temp, 0) + 1;
|
|
temp += 2;
|
|
if (temp[0] == ':')
|
|
temp++;
|
|
}
|
|
if (optC) {
|
|
rem = temp;
|
|
} else if (optT) {
|
|
if (temp && *temp && *(temp + 1) == ',') {
|
|
wait_char = *temp;
|
|
wait_sec = atoi(temp + 2);
|
|
} else
|
|
wait_sec = 0;
|
|
}
|
|
if (args > last)
|
|
args = NULL;
|
|
if (args) args = trim(args);
|
|
return true;
|
|
}
|
|
|
|
void DOS_Shell::CMD_CHOICE(char * args){
|
|
HELP("CHOICE");
|
|
static char defchoice[3] = {MSG_Get("INT21_6523_YESNO_CHARS")[0],MSG_Get("INT21_6523_YESNO_CHARS")[1],0};
|
|
//char *rem1 = NULL, *rem2 = NULL; /* unused */
|
|
char *rem = NULL, *temp = NULL, waitchar = 0, *ptr;
|
|
int waitsec = 0;
|
|
//bool optC = false, optT = false; /* unused */
|
|
bool optN = ScanCMDBool(args,"N");
|
|
bool optS = ScanCMDBool(args,"S"); //Case-sensitive matching
|
|
// ignore /b and /m switches for compatibility
|
|
ScanCMDBool(args,"B");
|
|
ScanCMDBool(args,"M"); // Text
|
|
ScanCMDBool(args,"T"); //Default Choice after timeout
|
|
|
|
while (args && *trim(args) == '/') {
|
|
if (!get_param(args, rem, temp, waitchar, waitsec)) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"), temp);
|
|
return;
|
|
}
|
|
}
|
|
if (!rem || !*rem) rem = defchoice; /* No choices specified use (national) YN */
|
|
ptr = rem;
|
|
uint8_t c;
|
|
if(!optS) while ((c = (uint8_t)(*ptr))) *ptr++ = (char)toupper(c); /* When in no case-sensitive mode. make everything upcase */
|
|
if(args && *args ) {
|
|
StripSpaces(args);
|
|
size_t argslen = strlen(args);
|
|
if(argslen>1 && args[0] == '"' && args[argslen-1] =='"') {
|
|
args[argslen-1] = 0; //Remove quotes
|
|
args++;
|
|
}
|
|
WriteOut(args);
|
|
}
|
|
/* Show question prompt of the form [a,b]? where a b are the choice values */
|
|
if (!optN) {
|
|
if(args && *args) WriteOut(" ");
|
|
WriteOut("[");
|
|
size_t len = strlen(rem);
|
|
for(size_t t = 1; t < len; t++) {
|
|
WriteOut("%c,",rem[t-1]);
|
|
}
|
|
WriteOut("%c]?",rem[len-1]);
|
|
}
|
|
|
|
// TO-DO: Find out how real DOS handles /T option for making a choice after delay; use AUTOTYPE for now
|
|
std::vector<std::string> sequence;
|
|
bool in_char = optS ? (strchr(rem, waitchar) != nullptr) : (strchr(rem, toupper(waitchar)) || strchr(rem, tolower(waitchar)));
|
|
if (waitchar && *rem && in_char && waitsec > 0) {
|
|
sequence.emplace_back(std::string(1, optS?waitchar:tolower(waitchar)));
|
|
MAPPER_AutoType(sequence, waitsec * 1000, 500, true);
|
|
}
|
|
uint16_t n=1;
|
|
do {
|
|
dotype = true;
|
|
DOS_ReadFile (STDIN,&c,&n);
|
|
dotype = false;
|
|
if (n==0) {dos.return_code=255;return;}
|
|
if (CheckBreak(this) || c==3) {dos.return_code=0;return;}
|
|
} while (!c || !(ptr = strchr(rem,(optS?c:toupper(c)))));
|
|
c = optS?c:(uint8_t)toupper(c);
|
|
DOS_WriteFile (STDOUT,&c, &n);
|
|
c = '\r'; DOS_WriteFile (STDOUT,&c, &n);
|
|
c = '\n'; DOS_WriteFile (STDOUT,&c, &n);
|
|
dos.return_code = (uint8_t)(ptr-rem+1);
|
|
}
|
|
|
|
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) {
|
|
char spath[DOS_PATHLENGTH],sargs[DOS_PATHLENGTH+4],path[DOS_PATHLENGTH+4],full[DOS_PATHLENGTH],sfull[DOS_PATHLENGTH+2];
|
|
if (!DOS_Canonicalize(args,full)||strrchr_dbcs(full,'\\')==NULL) {
|
|
shell->WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
|
|
if (!optS) ctrlbrk=true;
|
|
return false;
|
|
}
|
|
if (!DOS_GetSFNPath(args,spath,false)) {
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args);
|
|
if (!optS) ctrlbrk=true;
|
|
return false;
|
|
}
|
|
if (!uselfn||!DOS_GetSFNPath(args,sfull,true)) strcpy(sfull,full);
|
|
sprintf(sargs,"\"%s\"",spath);
|
|
bool found=false, res=DOS_FindFirst(sargs,0xffff & ~DOS_ATTR_VOLUME);
|
|
if (!res&&!optS) return false;
|
|
//end can't be 0, but if it is we'll get a nice crash, who cares :)
|
|
strcpy(path,full);
|
|
*(strrchr_dbcs(path,'\\')+1)=0;
|
|
char * end=strrchr_dbcs(full,'\\')+1;*end=0;
|
|
char * lend=strrchr_dbcs(sfull,'\\')+1;*lend=0;
|
|
char name[DOS_NAMELENGTH_ASCII],lname[LFN_NAMELENGTH+1];
|
|
uint32_t size,hsize;uint16_t time,date;uint8_t attr;uint16_t fattr;
|
|
while (res) {
|
|
if (CheckBreak(shell)) {ctrlbrk=true;return false;}
|
|
dta.GetResult(name,lname,size,hsize,date,time,attr);
|
|
if (!((!strcmp(name, ".") || !strcmp(name, "..") || strchr(sargs, '*')!=NULL || strchr(sargs, '?')!=NULL) && attr & DOS_ATTR_DIRECTORY)) {
|
|
found=true;
|
|
strcpy(end,name);
|
|
strcpy(lend,lname);
|
|
if (strlen(full)&&DOS_GetFileAttr(((uselfn||strchr(full, ' ')?(full[0]!='"'?"\"":""):"")+std::string(full)+(uselfn||strchr(full, ' ')?(full[strlen(full)-1]!='"'?"\"":""):"")).c_str(), &fattr)) {
|
|
bool attra=fattr&DOS_ATTR_ARCHIVE, attrs=fattr&DOS_ATTR_SYSTEM, attrh=fattr&DOS_ATTR_HIDDEN, attrr=fattr&DOS_ATTR_READ_ONLY;
|
|
if (adda||adds||addh||addr||suba||subs||subh||subr) {
|
|
if (adda) fattr|=DOS_ATTR_ARCHIVE;
|
|
if (adds) fattr|=DOS_ATTR_SYSTEM;
|
|
if (addh) fattr|=DOS_ATTR_HIDDEN;
|
|
if (addr) fattr|=DOS_ATTR_READ_ONLY;
|
|
if (suba) fattr&=~DOS_ATTR_ARCHIVE;
|
|
if (subs) fattr&=~DOS_ATTR_SYSTEM;
|
|
if (subh) fattr&=~DOS_ATTR_HIDDEN;
|
|
if (subr) fattr&=~DOS_ATTR_READ_ONLY;
|
|
if (DOS_SetFileAttr(((uselfn||strchr(full, ' ')?(full[0]!='"'?"\"":""):"")+std::string(full)+(uselfn||strchr(full, ' ')?(full[strlen(full)-1]!='"'?"\"":""):"")).c_str(), fattr)) {
|
|
if (DOS_GetFileAttr(((uselfn||strchr(full, ' ')?(full[0]!='"'?"\"":""):"")+std::string(full)+(uselfn||strchr(full, ' ')?(full[strlen(full)-1]!='"'?"\"":""):"")).c_str(), &fattr)) {
|
|
shell->WriteOut(" %c %c%c%c ", fattr&DOS_ATTR_ARCHIVE?'A':' ', fattr&DOS_ATTR_SYSTEM?'S':' ', fattr&DOS_ATTR_HIDDEN?'H':' ', fattr&DOS_ATTR_READ_ONLY?'R':' ');
|
|
shell->WriteOut_NoParsing(uselfn?sfull:full, true);
|
|
shell->WriteOut("\n");
|
|
}
|
|
} else
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_ATTRIB_SET_ERROR"),uselfn?sfull:full);
|
|
} else {
|
|
shell->WriteOut(" %c %c%c%c ", attra?'A':' ', attrs?'S':' ', attrh?'H':' ', attrr?'R':' ');
|
|
shell->WriteOut_NoParsing(uselfn?sfull:full, true);
|
|
shell->WriteOut("\n");
|
|
}
|
|
} else
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_ATTRIB_GET_ERROR"),uselfn?sfull:full);
|
|
}
|
|
res=DOS_FindNext();
|
|
}
|
|
if (optS) {
|
|
size_t len=strlen(path);
|
|
strcat(path, "*.*");
|
|
bool ret=DOS_FindFirst(path,0xffff & ~DOS_ATTR_VOLUME);
|
|
*(path+len)=0;
|
|
if (ret) {
|
|
std::vector<std::string> cdirs;
|
|
cdirs.clear();
|
|
do { /* File name and extension */
|
|
DtaResult result;
|
|
dta.GetResult(result.name,result.lname,result.size,result.hsize,result.date,result.time,result.attr);
|
|
|
|
if((result.attr&DOS_ATTR_DIRECTORY) && strcmp(result.name, ".")&&strcmp(result.name, "..")) {
|
|
strcat(path, result.name);
|
|
strcat(path, "\\");
|
|
char *fname = strrchr_dbcs(args, '\\');
|
|
if (fname!=NULL) fname++;
|
|
else {
|
|
fname = strrchr(args, ':');
|
|
if (fname!=NULL) fname++;
|
|
else fname=args;
|
|
}
|
|
strcat(path, fname);
|
|
cdirs.push_back((path[0]!='"'&&path[strlen(path)-1]=='"'?"\"":"")+std::string(path));
|
|
*(path+len)=0;
|
|
}
|
|
} while ( (ret=DOS_FindNext()) );
|
|
adirs.insert(adirs.begin()+1, cdirs.begin(), cdirs.end());
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
void DOS_Shell::CMD_ATTRIB(char *args){
|
|
HELP("ATTRIB");
|
|
StripSpaces(args);
|
|
|
|
bool optS=ScanCMDBool(args,"S");
|
|
char * rem=ScanCMDRemain(args);
|
|
if (rem) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
|
|
return;
|
|
}
|
|
|
|
bool adda=false, adds=false, addh=false, addr=false, suba=false, subs=false, subh=false, subr=false;
|
|
char sfull[DOS_PATHLENGTH+2];
|
|
char* arg1;
|
|
strcpy(sfull, "*.*");
|
|
do {
|
|
arg1=StripArg(args);
|
|
if (!strcasecmp(arg1, "+A")) adda=true;
|
|
else if (!strcasecmp(arg1, "+S")) adds=true;
|
|
else if (!strcasecmp(arg1, "+H")) addh=true;
|
|
else if (!strcasecmp(arg1, "+R")) addr=true;
|
|
else if (!strcasecmp(arg1, "-A")) suba=true;
|
|
else if (!strcasecmp(arg1, "-S")) subs=true;
|
|
else if (!strcasecmp(arg1, "-H")) subh=true;
|
|
else if (!strcasecmp(arg1, "-R")) subr=true;
|
|
else if (*arg1) {
|
|
strcpy(sfull, arg1);
|
|
if (uselfn&&strchr(sfull, '*')) {
|
|
char * find_last;
|
|
find_last=strrchr_dbcs(sfull,'\\');
|
|
if (find_last==NULL) find_last=sfull;
|
|
else find_last++;
|
|
if (sfull[strlen(sfull)-1]=='*'&&strchr(find_last, '.')==NULL) strcat(sfull, ".*");
|
|
}
|
|
}
|
|
} while (*args);
|
|
|
|
char buffer[CROSS_LEN];
|
|
args = ExpandDot(sfull,buffer, CROSS_LEN, false);
|
|
StripSpaces(args);
|
|
RealPt save_dta=dos.dta();
|
|
dos.dta(dos.tables.tempdta);
|
|
DOS_DTA dta(dos.dta());
|
|
adirs.clear();
|
|
adirs.emplace_back(std::string(args));
|
|
bool found=false;
|
|
inshell=true;
|
|
while (!adirs.empty()) {
|
|
ctrlbrk=false;
|
|
if (doAttrib(this, (char *)adirs.begin()->c_str(), dta, optS, adda, adds, addh, addr, suba, subs, subh, subr))
|
|
found=true;
|
|
else if (ctrlbrk)
|
|
break;
|
|
adirs.erase(adirs.begin());
|
|
}
|
|
if (!found&&!ctrlbrk) WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args);
|
|
inshell=false;
|
|
ctrlbrk=false;
|
|
dos.dta(save_dta);
|
|
}
|
|
|
|
void DOS_Shell::CMD_PROMPT(char *args){
|
|
HELP("PROMPT");
|
|
if(args && *args) {
|
|
args++;
|
|
SetEnv("PROMPT",args);
|
|
} else
|
|
SetEnv("PROMPT","$P$G");
|
|
return;
|
|
}
|
|
|
|
void DOS_Shell::CMD_PATH(char *args){
|
|
HELP("PATH");
|
|
if(args && *args){
|
|
char pathstring[DOS_PATHLENGTH+CROSS_LEN+20]={ 0 };
|
|
strcpy(pathstring,"set PATH=");
|
|
while(args && (*args=='='|| *args==' '))
|
|
args++;
|
|
if (strlen(args) == 1 && *args == ';')
|
|
*args = 0;
|
|
if (args) {
|
|
std::string vstr = args;
|
|
bool zdirpath = static_cast<Section_prop *>(control->GetSection("dos"))->Get_bool("drive z expand path");
|
|
if (zdirpath) GetExpandedPath(vstr);
|
|
strcat(pathstring,vstr.c_str());
|
|
}
|
|
this->ParseLine(pathstring);
|
|
return;
|
|
} else {
|
|
std::string line;
|
|
if(GetEnvStr("PATH",line)) {
|
|
WriteOut("%s\n",line.c_str());
|
|
} else {
|
|
WriteOut("PATH=(null)\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
void DOS_Shell::CMD_VERIFY(char * args) {
|
|
HELP("VERIFY");
|
|
args = trim(args);
|
|
if (!*args)
|
|
WriteOut("VERIFY is %s\n", dos.verify ? "on" : "off");
|
|
else if (!strcasecmp(args, "OFF"))
|
|
dos.verify = false;
|
|
else if (!strcasecmp(args, "ON"))
|
|
dos.verify = true;
|
|
else
|
|
WriteOut("Must specify ON or OFF\n");
|
|
}
|
|
|
|
void dos_ver_menu(bool start);
|
|
bool set_ver(char *s);
|
|
void DOS_Shell::CMD_VER(char *args) {
|
|
HELP("VER");
|
|
bool optR=ScanCMDBool(args,"R");
|
|
if (char* rem = ScanCMDRemain(args)) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"), rem);
|
|
return;
|
|
}
|
|
if(!optR && args && *args) {
|
|
char* word = StripWord(args);
|
|
if(strcasecmp(word,"set")) {
|
|
if (*word=='=') word=trim(word+1);
|
|
if (isdigit(*word)) {
|
|
if (*args) {
|
|
WriteOut(MSG_Get("SHELL_INVALID_PARAMETER"), args);
|
|
return;
|
|
}
|
|
if (set_ver(word))
|
|
dos_ver_menu(false);
|
|
else
|
|
WriteOut(MSG_Get("SHELL_CMD_VER_INVALID"));
|
|
return;
|
|
}
|
|
if (*word) {
|
|
WriteOut(MSG_Get("SHELL_INVALID_PARAMETER"), word);
|
|
return;
|
|
}
|
|
}
|
|
if (!*args) {
|
|
dos.version.major = 5;
|
|
dos.version.minor = 0;
|
|
} else if (!set_ver(args)) {
|
|
WriteOut(MSG_Get("SHELL_CMD_VER_INVALID"));
|
|
return;
|
|
}
|
|
dos_ver_menu(false);
|
|
} else {
|
|
WriteOut(MSG_Get("SHELL_CMD_VER_VER"),VERSION,SDL_STRING,dos.version.major,dos.version.minor);
|
|
if (optR) WriteOut("DOSBox-X Git commit %s, built on %s\nPlatform: %s %d-bit", GIT_COMMIT_HASH, UPDATED_STR, OS_PLATFORM, OS_BIT_INT);
|
|
}
|
|
}
|
|
|
|
void DOS_Shell::CMD_VOL(char *args){
|
|
HELP("VOL");
|
|
uint8_t drive=DOS_GetDefaultDrive();
|
|
if(args && *args){
|
|
args++;
|
|
uint32_t argLen = (uint32_t)strlen(args);
|
|
switch (args[argLen-1]) {
|
|
case ':' :
|
|
if(!strcasecmp(args,":")) return;
|
|
int drive2; drive2= toupper(*reinterpret_cast<unsigned char*>(&args[0]));
|
|
char * c; c = strchr(args,':'); *c = '\0';
|
|
if (Drives[drive2-'A']) drive = drive2 - 'A';
|
|
else {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_DRIVE"));
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
WriteOut(MSG_Get("SHELL_SYNTAXERROR"));
|
|
return;
|
|
}
|
|
}
|
|
char const* bufin = Drives[drive]->GetLabel();
|
|
if (tree)
|
|
WriteOut(MSG_Get("SHELL_CMD_VOL_TREE"),bufin);
|
|
else {
|
|
WriteOut(MSG_Get("SHELL_CMD_VOL_DRIVE"),drive+'A');
|
|
|
|
//if((drive+'A')=='Z') bufin="DOSBOX-X";
|
|
if(strcasecmp(bufin,"")==0)
|
|
WriteOut(MSG_Get("SHELL_CMD_VOL_SERIAL_NOLABEL"));
|
|
else
|
|
WriteOut(MSG_Get("SHELL_CMD_VOL_SERIAL_LABEL"),bufin);
|
|
}
|
|
|
|
WriteOut(tree?MSG_Get("SHELL_CMD_VOL_SERIAL")+1:MSG_Get("SHELL_CMD_VOL_SERIAL"));
|
|
unsigned long serial_number=0x1234;
|
|
if (!strncmp(Drives[drive]->GetInfo(),"fatDrive ",9)) {
|
|
fatDrive* fdp = dynamic_cast<fatDrive*>(Drives[drive]);
|
|
if (fdp != NULL) serial_number=fdp->GetSerial();
|
|
}
|
|
#if defined (WIN32)
|
|
if (!strncmp(Drives[drive]->GetInfo(),"local ",6) || !strncmp(Drives[drive]->GetInfo(),"CDRom ",6)) {
|
|
localDrive* ldp = !strncmp(Drives[drive]->GetInfo(),"local ",6)?dynamic_cast<localDrive*>(Drives[drive]):dynamic_cast<cdromDrive*>(Drives[drive]);
|
|
if (ldp != NULL) serial_number=ldp->GetSerial();
|
|
}
|
|
#endif
|
|
WriteOut("%04X-%04X\n", serial_number/0x10000, serial_number%0x10000);
|
|
return;
|
|
}
|
|
|
|
void DOS_Shell::CMD_TRUENAME(char * args) {
|
|
HELP("TRUENAME");
|
|
bool optH=ScanCMDBool(args,"H");
|
|
args = trim(args);
|
|
if (!*args) {
|
|
WriteOut("No file name given.\n");
|
|
return;
|
|
}
|
|
if (char* rem = ScanCMDRemain(args)) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"), rem);
|
|
return;
|
|
}
|
|
char *name = StripArg(args), fullname[DOS_PATHLENGTH];
|
|
uint8_t drive;
|
|
if (DOS_MakeName(name, fullname, &drive)) {
|
|
if (optH) {
|
|
if (!strncmp(Drives[drive]->GetInfo(),"local ",6) || !strncmp(Drives[drive]->GetInfo(),"CDRom ",6)) {
|
|
localDrive *ldp = dynamic_cast<localDrive*>(Drives[drive]);
|
|
Overlay_Drive *odp = dynamic_cast<Overlay_Drive*>(Drives[drive]);
|
|
std::string hostname = "";
|
|
if (odp) hostname = odp->GetHostName(fullname);
|
|
else if (ldp) hostname = ldp->GetHostName(fullname);
|
|
if (hostname.size()) {
|
|
WriteOut_NoParsing(hostname.c_str(), true);
|
|
WriteOut("\n");
|
|
}
|
|
}
|
|
} else
|
|
#if defined(WIN32) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
|
|
if (Network_IsNetworkResource(fullname)) {
|
|
WriteOut_NoParsing(name, true);
|
|
WriteOut("\r\n");
|
|
} else
|
|
#endif
|
|
{
|
|
WriteOut("%c:\\", drive+'A');
|
|
WriteOut_NoParsing(fullname, true);
|
|
WriteOut("\r\n");
|
|
}
|
|
}
|
|
else
|
|
WriteOut(dos.errorcode==DOSERR_PATH_NOT_FOUND?"Path not found\n":"File not found\n");
|
|
}
|
|
|
|
void SetVal(const std::string& secname, const std::string& preval, const std::string& val);
|
|
static void delayed_press(Bitu key) { KEYBOARD_AddKey((KBD_KEYS)key,true); }
|
|
static void delayed_release(Bitu key) { KEYBOARD_AddKey((KBD_KEYS)key,false); }
|
|
static void delayed_sdlpress(Bitu core) {
|
|
if(core==1) SetVal("cpu","core","normal");
|
|
else if(core==2) SetVal("cpu","core","simple");
|
|
else if(core==3) SetVal("cpu","core","dynamic");
|
|
else if(core==4) SetVal("cpu","core","full");
|
|
}
|
|
// ADDKEY patch was created by Moe
|
|
void DOS_Shell::CMD_ADDKEY(char * args){
|
|
//HELP("ADDKEY");
|
|
StripSpaces(args);
|
|
if (!*args) {
|
|
WriteOut(MSG_Get("SHELL_SYNTAXERROR"));
|
|
return;
|
|
}
|
|
pic_tickindex_t delay = 0;
|
|
int duration = 0, core = 0;
|
|
|
|
while (*args) {
|
|
char *word=StripWord(args);
|
|
KBD_KEYS scankey = (KBD_KEYS)0;
|
|
char *tail;
|
|
bool alt = false, ctrl = false, shift = false;
|
|
while (word[1] == '-') {
|
|
switch (word[0]) {
|
|
case 'c':
|
|
ctrl = true;
|
|
word += 2;
|
|
break;
|
|
case 's':
|
|
shift = true;
|
|
word += 2;
|
|
break;
|
|
case 'a':
|
|
alt = true;
|
|
word += 2;
|
|
break;
|
|
default:
|
|
WriteOut(MSG_Get("SHELL_SYNTAXERROR"));
|
|
return;
|
|
}
|
|
}
|
|
if (!strcasecmp(word,"enter")) {
|
|
word[0] = (char)10;
|
|
word[1] = (char)0;
|
|
} else if (!strcasecmp(word,"space")) {
|
|
word[0] = (char)32;
|
|
word[1] = (char)0;
|
|
} else if (!strcasecmp(word,"bs")) {
|
|
word[0] = (char)8;
|
|
word[1] = (char)0;
|
|
} else if (!strcasecmp(word,"tab")) {
|
|
word[0] = (char)9;
|
|
word[1] = (char)0;
|
|
} else if (!strcasecmp(word,"escape")) {
|
|
word[0] = (char)27;
|
|
word[1] = (char)0;
|
|
} else if (!strcasecmp(word,"up")) {
|
|
word[0] = (char)141;
|
|
word[1] = (char)0;
|
|
} else if (!strcasecmp(word,"down")) {
|
|
word[0] = (char)142;
|
|
word[1] = (char)0;
|
|
} else if (!strcasecmp(word,"left")) {
|
|
word[0] = (char)143;
|
|
word[1] = (char)0;
|
|
} else if (!strcasecmp(word,"right")) {
|
|
word[0] = (char)144;
|
|
word[1] = (char)0;
|
|
} else if (!strcasecmp(word,"ins")) {
|
|
word[0] = (char)145;
|
|
word[1] = (char)0;
|
|
} else if (!strcasecmp(word,"del")) {
|
|
word[0] = (char)146;
|
|
word[1] = (char)0;
|
|
} else if (!strcasecmp(word,"home")) {
|
|
word[0] = (char)147;
|
|
word[1] = (char)0;
|
|
} else if (!strcasecmp(word,"end")) {
|
|
word[0] = (char)148;
|
|
word[1] = (char)0;
|
|
} else if (!strcasecmp(word,"pgup")) {
|
|
word[0] = (char)149;
|
|
word[1] = (char)0;
|
|
} else if (!strcasecmp(word,"pgdown")) {
|
|
word[0] = (char)150;
|
|
word[1] = (char)0;
|
|
} else if (!strcasecmp(word,"normal")) {
|
|
core = 1;
|
|
} else if (!strcasecmp(word,"simple")) {
|
|
core = 2;
|
|
} else if (!strcasecmp(word,"dynamic")) {
|
|
core = 3;
|
|
} else if (!strcasecmp(word,"full")) {
|
|
core = 4;
|
|
} else if (word[0] == 'k' && word[1] == 'p' && word[2] && !word[3]) {
|
|
word[0] = 151+word[2]-'0';
|
|
word[1] = 0;
|
|
} else if (word[0] == 'f' && word[1]) {
|
|
word[0] = 128+word[1]-'0';
|
|
if (word[1] == '1' && word[2]) word[0] = 128+word[2]-'0'+10;
|
|
word[1] = 0;
|
|
}
|
|
if (!word[1]) {
|
|
const int shiftflag = 0x1000000;
|
|
const int map[256] = {
|
|
0,0,0,0,0,0,0,0,
|
|
KBD_backspace,
|
|
KBD_tab,
|
|
KBD_enter,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
KBD_esc,
|
|
0,0,0,0,
|
|
KBD_space, KBD_1|shiftflag, KBD_quote|shiftflag, KBD_3|shiftflag, KBD_4|shiftflag, KBD_5|shiftflag, KBD_7|shiftflag, KBD_quote,
|
|
KBD_9|shiftflag, KBD_0|shiftflag, KBD_8|shiftflag, KBD_equals|shiftflag, KBD_comma, KBD_minus, KBD_period, KBD_slash,
|
|
KBD_0, KBD_1, KBD_2, KBD_3, KBD_4, KBD_5, KBD_6, KBD_7,
|
|
KBD_8, KBD_9, KBD_semicolon|shiftflag, KBD_semicolon, KBD_comma|shiftflag, KBD_equals, KBD_period|shiftflag, KBD_slash|shiftflag,
|
|
KBD_2|shiftflag, KBD_a|shiftflag, KBD_b|shiftflag, KBD_c|shiftflag, KBD_d|shiftflag, KBD_e|shiftflag, KBD_f|shiftflag, KBD_g|shiftflag,
|
|
KBD_h|shiftflag, KBD_i|shiftflag, KBD_j|shiftflag, KBD_k|shiftflag, KBD_l|shiftflag, KBD_m|shiftflag, KBD_n|shiftflag, KBD_o|shiftflag,
|
|
KBD_p|shiftflag, KBD_q|shiftflag, KBD_r|shiftflag, KBD_s|shiftflag, KBD_t|shiftflag, KBD_u|shiftflag, KBD_v|shiftflag, KBD_w|shiftflag,
|
|
KBD_x|shiftflag, KBD_y|shiftflag, KBD_z|shiftflag, KBD_leftbracket, KBD_backslash, KBD_rightbracket, KBD_6|shiftflag, KBD_minus|shiftflag,
|
|
KBD_grave, KBD_a, KBD_b, KBD_c, KBD_d, KBD_e, KBD_f, KBD_g,
|
|
KBD_h, KBD_i, KBD_j, KBD_k, KBD_l, KBD_m, KBD_n, KBD_o,
|
|
KBD_p, KBD_q, KBD_r, KBD_s, KBD_t, KBD_u, KBD_v, KBD_w,
|
|
KBD_x, KBD_y, KBD_z, KBD_leftbracket|shiftflag, KBD_backslash|shiftflag, KBD_rightbracket|shiftflag, KBD_grave|shiftflag, 0,
|
|
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,
|
|
KBD_up, KBD_down, KBD_left, KBD_right, KBD_insert, KBD_delete, KBD_home, KBD_end, KBD_pageup, KBD_pagedown,
|
|
KBD_kp0, KBD_kp1, KBD_kp2, KBD_kp3, KBD_kp4, KBD_kp5, KBD_kp6, KBD_kp7, KBD_kp8, KBD_kp9,
|
|
};
|
|
scankey = (KBD_KEYS)(map[(unsigned char)word[0]] & ~shiftflag);
|
|
if (map[(unsigned char)word[0]] & shiftflag) shift = true;
|
|
if (!scankey && core == 0) {
|
|
WriteOut(MSG_Get("SHELL_SYNTAXERROR"),word);
|
|
return;
|
|
}
|
|
if (core == 0) word[0] = 0;
|
|
}
|
|
if (word[0] == 'p') {
|
|
delay += strtol(word+1,&tail,0);
|
|
if (tail && *tail) {
|
|
WriteOut(MSG_Get("SHELL_SYNTAXERROR"),word);
|
|
return;
|
|
}
|
|
} else if (word[0] == 'l') {
|
|
duration = strtol(word+1,&tail,0);
|
|
if (tail && *tail) {
|
|
WriteOut(MSG_Get("SHELL_SYNTAXERROR"),word);
|
|
return;
|
|
}
|
|
} else if (!word[0] || ((scankey = (KBD_KEYS)strtol(word,NULL,0)) > KBD_NONE && scankey < KBD_LAST)) {
|
|
if (shift) {
|
|
if (delay == 0) KEYBOARD_AddKey(KBD_leftshift,true);
|
|
else PIC_AddEvent(&delayed_press,delay++,KBD_leftshift);
|
|
}
|
|
if (ctrl) {
|
|
if (delay == 0) KEYBOARD_AddKey(KBD_leftctrl,true);
|
|
else PIC_AddEvent(&delayed_press,delay++,KBD_leftctrl);
|
|
}
|
|
if (alt) {
|
|
if (delay == 0) KEYBOARD_AddKey(KBD_leftalt,true);
|
|
else PIC_AddEvent(&delayed_press,delay++,KBD_leftalt);
|
|
}
|
|
if (delay == 0) KEYBOARD_AddKey(scankey,true);
|
|
else PIC_AddEvent(&delayed_press,delay++,scankey);
|
|
|
|
if (delay+duration == 0) KEYBOARD_AddKey(scankey,false);
|
|
else PIC_AddEvent(&delayed_release,delay+++duration,scankey);
|
|
if (alt) {
|
|
if (delay+duration == 0) KEYBOARD_AddKey(KBD_leftalt,false);
|
|
else PIC_AddEvent(&delayed_release,delay+++duration,KBD_leftalt);
|
|
}
|
|
if (ctrl) {
|
|
if (delay+duration == 0) KEYBOARD_AddKey(KBD_leftctrl,false);
|
|
else PIC_AddEvent(&delayed_release,delay+++duration,KBD_leftctrl);
|
|
}
|
|
if (shift) {
|
|
if (delay+duration == 0) KEYBOARD_AddKey(KBD_leftshift,false);
|
|
else PIC_AddEvent(&delayed_release,delay+++duration,KBD_leftshift);
|
|
}
|
|
} else if (core != 0) {
|
|
if (core == 1) {
|
|
if (delay == 0) SetVal("cpu","core","normal");
|
|
else PIC_AddEvent(&delayed_sdlpress,delay++,1);
|
|
} else if (core == 2) {
|
|
if (delay == 0) SetVal("cpu","core","simple");
|
|
else PIC_AddEvent(&delayed_sdlpress,delay++,2);
|
|
} else if (core == 3) {
|
|
if (delay == 0) SetVal("cpu","core","dynamic");
|
|
else PIC_AddEvent(&delayed_sdlpress,delay++,3);
|
|
} else if (core == 4) {
|
|
if (delay == 0) SetVal("cpu","core","full");
|
|
else PIC_AddEvent(&delayed_sdlpress,delay++,4);
|
|
}
|
|
} else {
|
|
WriteOut(MSG_Get("SHELL_SYNTAXERROR"),word);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if C_DEBUG
|
|
extern bool tohide;
|
|
bool debugger_break_on_exec = false;
|
|
void DEBUG_Enable_Handler(bool pressed);
|
|
void DOS_Shell::CMD_DEBUGBOX(char * args) {
|
|
while (*args == ' ') args++;
|
|
std::string argv=std::string(args);
|
|
args=StripArg(args);
|
|
HELP("DEBUGBOX");
|
|
/* TODO: The command as originally taken from DOSBox SVN supported a /NOMOUSE option to remove the INT 33h vector */
|
|
if (!*args) {
|
|
tohide=false;
|
|
DEBUG_Enable_Handler(true);
|
|
tohide=true;
|
|
return;
|
|
} else if (!strcmp(args,"-?")) {
|
|
args[0]='/';
|
|
HELP("DEBUGBOX");
|
|
return;
|
|
}
|
|
debugger_break_on_exec = true;
|
|
DoCommand((char *)argv.c_str());
|
|
debugger_break_on_exec = false;
|
|
}
|
|
#endif
|
|
|
|
char *str_replace(const char *orig, const char *rep, const char *with) {
|
|
char *result, *ins, *tmp;
|
|
size_t len_rep, len_with, len_front;
|
|
int count;
|
|
|
|
if (!orig || !rep) return NULL;
|
|
|
|
char* mutable_orig = strdup(orig); // Make a mutable copy of orig
|
|
char* original_mutable_orig = mutable_orig; // Store the original address for freeing below
|
|
|
|
len_rep = strlen(rep);
|
|
if (len_rep == 0) return NULL;
|
|
len_with = with?strlen(with):0;
|
|
|
|
ins = mutable_orig;
|
|
for (count = 0; (tmp = strstr(ins, rep)) != NULL; ++count)
|
|
ins = tmp + len_rep;
|
|
|
|
tmp = result = (char *)malloc(strlen(mutable_orig) + (len_with - len_rep) * count + 1);
|
|
if (!result) return NULL;
|
|
|
|
while (count--) {
|
|
ins = strstr(mutable_orig, rep);
|
|
len_front = ins - mutable_orig;
|
|
tmp = strncpy(tmp, mutable_orig, len_front) + len_front;
|
|
tmp = strcpy(tmp, with?with:"") + len_with;
|
|
mutable_orig += len_front + len_rep;
|
|
}
|
|
strcpy(tmp, mutable_orig);
|
|
free(original_mutable_orig);
|
|
return result;
|
|
}
|
|
|
|
void DOS_Shell::CMD_FOR(char *args) {
|
|
HELP("FOR");
|
|
args = ltrim(args);
|
|
if (strlen(args)<12){SyntaxError();return;}
|
|
char s[3];
|
|
strcpy(s,"%%");
|
|
if (*args=='%' && (isalpha(args[1]) || isdigit(args[1]) || strchr("_-/*.;#$",args[1])) && isspace(args[2]))
|
|
s[1]=*(args+1);
|
|
else{SyntaxError();return;}
|
|
args = ltrim(args+3);
|
|
if (strncasecmp(args, "IN", 2) || !isspace(args[2])){SyntaxError();return;}
|
|
args = ltrim(args+3);
|
|
if (*args=='(')
|
|
args = ltrim(args+1);
|
|
else{SyntaxError();return;}
|
|
char *p=strchr(args, ')');
|
|
if (p==NULL||!isspace(*(p+1))){SyntaxError();return;}
|
|
*p=0;
|
|
char flist[260], *fp=flist;
|
|
if (strlen(ltrim(args))<260)
|
|
strcpy(flist, ltrim(args));
|
|
else
|
|
{
|
|
strncpy(flist, args, 259);
|
|
flist[259]=0;
|
|
}
|
|
*p=')';
|
|
args=ltrim(p+2);
|
|
if (strncasecmp(args, "DO", 2) || !isspace(args[2])){SyntaxError();return;}
|
|
args = ltrim(args+3);
|
|
bool lfn=uselfn&&lfnfor;
|
|
while (*fp) {
|
|
p=fp;
|
|
int q=0;
|
|
while (*p&&(q/2*2!=q||(*p!=' '&&*p!=','&&*p!=';')))
|
|
{
|
|
if (*p=='"')
|
|
q++;
|
|
p++;
|
|
}
|
|
bool last=!!strlen(p);
|
|
if (last) *p=0;
|
|
if (strchr(fp, '?') || strchr(fp, '*')) {
|
|
char name[DOS_NAMELENGTH_ASCII], lname[LFN_NAMELENGTH], spath[DOS_PATHLENGTH], path[DOS_PATHLENGTH], pattern[DOS_PATHLENGTH], full[DOS_PATHLENGTH], *r;
|
|
if (!DOS_Canonicalize(fp,full)) return;
|
|
r=strrchr_dbcs(full, '\\');
|
|
if (r!=NULL) {
|
|
*r=0;
|
|
strcpy(path, full);
|
|
strcat(path, "\\");
|
|
strcpy(pattern, r+1);
|
|
*r='\\';
|
|
} else {
|
|
strcpy(path, "");
|
|
strcpy(pattern, full);
|
|
}
|
|
strcpy(spath, path);
|
|
if (strchr(fp,'\"')||uselfn) {
|
|
if (!DOS_GetSFNPath(("\""+std::string(path)+"\\").c_str(), spath, false)) strcpy(spath, path);
|
|
if (!strlen(spath)||spath[strlen(spath)-1]!='\\') strcat(spath, "\\");
|
|
int k=0;
|
|
for (int i=0;i<(int)strlen(path);i++)
|
|
if (path[i]!='\"')
|
|
path[k++]=path[i];
|
|
path[k]=0;
|
|
}
|
|
uint32_t size, hsize;
|
|
uint16_t date, time;
|
|
uint8_t attr;
|
|
DOS_DTA dta(dos.dta());
|
|
std::vector<std::string> sources;
|
|
std::string tmp;
|
|
int fbak=lfn_filefind_handle;
|
|
lfn_filefind_handle=lfn?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE;
|
|
if (DOS_FindFirst((std::string(spath)+std::string(pattern)).c_str(), ~(DOS_ATTR_VOLUME|DOS_ATTR_DIRECTORY|DOS_ATTR_DEVICE|DOS_ATTR_HIDDEN|DOS_ATTR_SYSTEM)))
|
|
{
|
|
dta.GetResult(name, lname, size, hsize, date, time, attr);
|
|
tmp=std::string(path)+std::string(lfn?lname:name);
|
|
sources.push_back(tmp);
|
|
while (DOS_FindNext())
|
|
{
|
|
dta.GetResult(name, lname, size, hsize, date, time, attr);
|
|
tmp=std::string(path)+std::string(lfn?lname:name);
|
|
sources.push_back(tmp);
|
|
}
|
|
}
|
|
lfn_filefind_handle=fbak;
|
|
for (std::vector<std::string>::iterator source = sources.begin(); source != sources.end(); ++source)
|
|
DoCommand(str_replace(args, s, source->c_str()));
|
|
} else
|
|
DoCommand(str_replace(args, s, fp));
|
|
if (last) *p=' ';
|
|
fp=ltrim(p);
|
|
}
|
|
}
|
|
|
|
void DOS_Shell::CMD_LFNFOR(char * args) {
|
|
HELP("LFNFOR");
|
|
args = trim(args);
|
|
if (!*args)
|
|
WriteOut("LFNFOR is %s\n", lfnfor ? "on" : "off");
|
|
else if (!strcasecmp(args, "OFF"))
|
|
lfnfor = false;
|
|
else if (!strcasecmp(args, "ON"))
|
|
lfnfor = true;
|
|
else
|
|
WriteOut("Must specify ON or OFF\n");
|
|
}
|
|
|
|
void DOS_Shell::CMD_ALIAS(char* args) {
|
|
HELP("ALIAS");
|
|
args = trim(args);
|
|
if (!*args || strchr(args, '=') == NULL) {
|
|
for (cmd_alias_map_t::iterator iter = cmd_alias.begin(), end = cmd_alias.end(); iter != end; ++iter) {
|
|
if (!*args || !strcasecmp(args, iter->first.c_str()))
|
|
WriteOut("ALIAS %s='%s'\n", iter->first.c_str(), iter->second.c_str());
|
|
}
|
|
} else {
|
|
char alias_name[256] = { 0 };
|
|
char* cmd = 0;
|
|
for (unsigned int offset = 0; *args && offset < sizeof(alias_name)-1; ++offset, ++args) {
|
|
if (*args == '=') {
|
|
cmd = trim(alias_name);
|
|
++args;
|
|
args = trim(args);
|
|
size_t args_len = strlen(args);
|
|
if ((*args == '"' && args[args_len - 1] == '"') || (*args == '\'' && args[args_len - 1] == '\'')) {
|
|
args[args_len - 1] = 0;
|
|
++args;
|
|
}
|
|
if (!*args) {
|
|
cmd_alias.erase(cmd);
|
|
} else {
|
|
cmd_alias[cmd] = args;
|
|
cmd_alias_map_t::iterator iter = cmd_alias.find(cmd);
|
|
if (iter != cmd_alias.end()) WriteOut("ALIAS %s='%s'\n", iter->first.c_str(), iter->second.c_str());
|
|
}
|
|
break;
|
|
} else {
|
|
alias_name[offset] = *args;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DOS_Shell::CMD_ASSOC(char* args) {
|
|
HELP("ASSOC");
|
|
args = trim(args);
|
|
if (!*args || strchr(args, '=') == NULL) {
|
|
for (cmd_assoc_map_t::iterator iter = cmd_assoc.begin(), end = cmd_assoc.end(); iter != end; ++iter) {
|
|
if (!*args || !strcasecmp(args, iter->first.c_str()))
|
|
WriteOut("%s=%s\n", iter->first.c_str(), iter->second.c_str());
|
|
}
|
|
} else {
|
|
char assoc_name[256] = { 0 };
|
|
char* cmd = 0;
|
|
for (unsigned int offset = 0; *args && offset < sizeof(assoc_name)-1; ++offset, ++args) {
|
|
if (*args == '=') {
|
|
cmd = trim(assoc_name);
|
|
if (!*cmd || cmd[0] != '.') {
|
|
WriteOut(MSG_Get("SHELL_INVALID_PARAMETER"), cmd);
|
|
break;
|
|
}
|
|
++args;
|
|
args = trim(args);
|
|
size_t args_len = strlen(args);
|
|
if ((*args == '"' && args[args_len - 1] == '"') || (*args == '\'' && args[args_len - 1] == '\'')) {
|
|
args[args_len - 1] = 0;
|
|
++args;
|
|
}
|
|
if (!*args) {
|
|
cmd_assoc.erase(cmd);
|
|
} else {
|
|
cmd_assoc[cmd] = args;
|
|
cmd_assoc_map_t::iterator iter = cmd_assoc.find(cmd);
|
|
if (iter != cmd_assoc.end()) WriteOut("%s=%s\n", iter->first.c_str(), iter->second.c_str());
|
|
}
|
|
break;
|
|
} else {
|
|
assoc_name[offset] = *args;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DOS_Shell::CMD_HISTORY(char* args) {
|
|
HELP("HISTORY");
|
|
if (ScanCMDBool(args,"C"))
|
|
l_history.clear();
|
|
for (auto it = l_history.rbegin(); it != l_history.rend(); ++it) {
|
|
WriteOut_NoParsing(it->c_str(), true);
|
|
WriteOut("\n");
|
|
}
|
|
}
|
|
|
|
void CAPTURE_StartCapture(void);
|
|
void CAPTURE_StopCapture(void);
|
|
|
|
void CAPTURE_StartWave(void);
|
|
void CAPTURE_StopWave(void);
|
|
|
|
void CAPTURE_StartMTWave(void);
|
|
void CAPTURE_StopMTWave(void);
|
|
|
|
void CAPTURE_StartOPL(void);
|
|
void CAPTURE_StopOPL(void);
|
|
|
|
// Explanation: Start capture, run program, stop capture when program exits.
|
|
// Great for gameplay footage or demoscene capture.
|
|
//
|
|
// The command name is chosen not to conform to the 8.3 pattern
|
|
// on purpose to avoid conflicts with any existing DOS applications.
|
|
void DOS_Shell::CMD_DXCAPTURE(char * args) {
|
|
while (*args == ' ') args++;
|
|
std::string argv=std::string(args);
|
|
args=StripArg(args);
|
|
HELP("DXCAPTURE");
|
|
bool cap_video = false;
|
|
bool cap_audio = false;
|
|
bool cap_mtaudio = false;
|
|
bool cap_opl = false;
|
|
unsigned long post_exit_delay_ms = 3000; /* 3 sec */
|
|
|
|
if (!strcmp(args,"-?")) {
|
|
args[0]='/';
|
|
HELP("DXCAPTURE");
|
|
return;
|
|
}
|
|
|
|
args=(char *)argv.c_str();
|
|
char *arg1;
|
|
while (strlen(args)&&args[0]=='/') {
|
|
arg1=StripArg(args);
|
|
upcase(arg1);
|
|
if (!(strcmp(arg1,"/V")))
|
|
cap_video = true;
|
|
else if (!(strcmp(arg1,"/-V")))
|
|
cap_video = false;
|
|
else if (!(strcmp(arg1,"/A")))
|
|
cap_audio = true;
|
|
else if (!(strcmp(arg1,"/O")))
|
|
cap_opl = true;
|
|
else if (!(strcmp(arg1,"/-A")))
|
|
cap_audio = false;
|
|
else if (!(strcmp(arg1,"/-O")))
|
|
cap_opl = false;
|
|
else if (!(strcmp(arg1,"/M")))
|
|
cap_mtaudio = true;
|
|
else if (!(strcmp(arg1,"/-M")))
|
|
cap_mtaudio = false;
|
|
else {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),arg1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!cap_video && !cap_audio && !cap_mtaudio && !cap_opl)
|
|
cap_video = true;
|
|
|
|
if (cap_video)
|
|
CAPTURE_StartCapture();
|
|
if (cap_audio)
|
|
CAPTURE_StartWave();
|
|
if (cap_mtaudio)
|
|
CAPTURE_StartMTWave();
|
|
if (cap_opl)
|
|
CAPTURE_StartOPL();
|
|
|
|
DoCommand(args);
|
|
|
|
if (post_exit_delay_ms > 0) {
|
|
LOG_MSG("Pausing for post exit delay (%.3f seconds)",(double)post_exit_delay_ms / 1000);
|
|
|
|
uint32_t lasttick=GetTicks();
|
|
while ((GetTicks()-lasttick)<post_exit_delay_ms) {
|
|
CALLBACK_Idle();
|
|
|
|
if (machine == MCH_PC98) {
|
|
reg_eax = 0x0100; // sense key
|
|
CALLBACK_RunRealInt(0x18);
|
|
SETFLAGBIT(ZF,reg_bh == 0);
|
|
}
|
|
else {
|
|
reg_eax = 0x0100;
|
|
CALLBACK_RunRealInt(0x16);
|
|
}
|
|
|
|
if (!GETFLAG(ZF)) {
|
|
if (machine == MCH_PC98) {
|
|
reg_eax = 0x0000; // read key
|
|
CALLBACK_RunRealInt(0x18);
|
|
}
|
|
else {
|
|
reg_eax = 0x0000;
|
|
CALLBACK_RunRealInt(0x16);
|
|
}
|
|
|
|
if (reg_al == 32/*space*/ || reg_al == 27/*escape*/)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cap_video)
|
|
CAPTURE_StopCapture();
|
|
if (cap_audio)
|
|
CAPTURE_StopWave();
|
|
if (cap_mtaudio)
|
|
CAPTURE_StopMTWave();
|
|
if (cap_opl)
|
|
CAPTURE_StopOPL();
|
|
}
|
|
|
|
void DOS_Shell::CMD_CTTY(char * args) {
|
|
HELP("CTTY");
|
|
/* NTS: This is written to emulate the simplistic parsing in MS-DOS 6.22 */
|
|
uint16_t handle;
|
|
int i;
|
|
|
|
/* args has leading space? */
|
|
args = trim(args);
|
|
|
|
/* must be device */
|
|
if (DOS_FindDevice(args) == DOS_DEVICES) {
|
|
WriteOut("Invalid device - %s\n", args);
|
|
return;
|
|
}
|
|
|
|
/* close STDIN/STDOUT/STDERR and replace with new handle */
|
|
if (!DOS_OpenFile(args,OPEN_READWRITE,&handle)) {
|
|
WriteOut("Unable to open device - %s\n", args);
|
|
return;
|
|
}
|
|
|
|
for (i=0;i < 3;i++) {
|
|
DOS_CloseFile(i);
|
|
DOS_ForceDuplicateEntry(handle,i);
|
|
}
|
|
DOS_CloseFile(handle);
|
|
}
|
|
|
|
void DOS_Shell::CMD_COUNTRY(char * args) {
|
|
HELP("COUNTRY");
|
|
if (char* rem = ScanCMDRemain(args)) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"), rem);
|
|
return;
|
|
}
|
|
args = trim(args);
|
|
if (!*args) {
|
|
WriteOut("Current country code: %d\n", countryNo);
|
|
return;
|
|
}
|
|
int newCC;
|
|
char buffer[256];
|
|
if (sscanf(args, "%d%s", &newCC, buffer) == 1 && newCC>0) {
|
|
countryNo = newCC;
|
|
DOS_SetCountry(countryNo);
|
|
return;
|
|
}
|
|
WriteOut("Invalid country code - %s\n", StripArg(args));
|
|
return;
|
|
}
|
|
|
|
extern bool jfont_init, isDBCSCP();
|
|
extern Bitu DOS_LoadKeyboardLayout(const char * layoutname, int32_t codepage, const char * codepagefile);
|
|
void runRescan(const char *str), MSG_Init(), JFONT_Init(), InitFontHandle(), ShutFontHandle(), initcodepagefont(), DOSBox_SetSysMenu();
|
|
int toSetCodePage(DOS_Shell *shell, int newCP, int opt) {
|
|
if (isSupportedCP(newCP)) {
|
|
dos.loaded_codepage = newCP;
|
|
int missing = 0;
|
|
#if defined(USE_TTF)
|
|
missing = TTF_using() ? setTTFCodePage() : 0;
|
|
#endif
|
|
if (!TTF_using()) initcodepagefont();
|
|
if (dos.loaded_codepage==437) DOS_LoadKeyboardLayout("us", 437, "auto");
|
|
if (opt==-1) {
|
|
MSG_Init();
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
mainMenu.unbuild();
|
|
mainMenu.rebuild();
|
|
if (!GFX_GetPreventFullscreen()) {
|
|
if (menu.toggle) DOSBox_SetMenu(); else DOSBox_NoMenu();
|
|
}
|
|
#endif
|
|
DOSBox_SetSysMenu();
|
|
}
|
|
if (isDBCSCP()) {
|
|
ShutFontHandle();
|
|
InitFontHandle();
|
|
JFONT_Init();
|
|
}
|
|
SetupDBCSTable();
|
|
runRescan("-A -Q");
|
|
#if defined(USE_TTF)
|
|
if ((opt==-1||opt==-2)&&TTF_using()&&(newCP==932||newCP==936||newCP==949||newCP==950||newCP==951)) {
|
|
Section_prop * ttf_section = static_cast<Section_prop *>(control->GetSection("ttf"));
|
|
const char *font = ttf_section->Get_string("font");
|
|
if (!font || !*font) {
|
|
ttf_reset();
|
|
#if C_PRINTER
|
|
if (printfont) UpdateDefaultPrinterFont();
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
return missing;
|
|
} else if (opt<1 && shell) {
|
|
shell->WriteOut(MSG_Get("SHELL_CMD_CHCP_INVALID"), std::to_string(newCP).c_str());
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
const char* DOS_GetLoadedLayout(void);
|
|
Bitu DOS_ChangeCodepage(int32_t codepage, const char* codepagefile);
|
|
Bitu DOS_ChangeKeyboardLayout(const char* layoutname, int32_t codepage);
|
|
|
|
void DOS_Shell::CMD_CHCP(char * args) {
|
|
HELP("CHCP");
|
|
args = trim(args);
|
|
if (!*args) {
|
|
WriteOut(MSG_Get("SHELL_CMD_CHCP_ACTIVE"), dos.loaded_codepage);
|
|
return;
|
|
}
|
|
if (IS_PC98_ARCH || IS_JEGA_ARCH) {
|
|
WriteOut("Changing code page is not supported for the PC-98 or JEGA/AX system.\n");
|
|
return;
|
|
}
|
|
if (IS_DOSV || IS_J3100)
|
|
{
|
|
WriteOut("Changing code page is not supported for the DOS/V or J-3100 system.\n");
|
|
return;
|
|
}
|
|
int32_t newCP;
|
|
char buff[256], *r;
|
|
int missing = 0, n = sscanf(args, "%d%s", &newCP, buff);
|
|
auto iter = langcp_map.find(newCP);
|
|
const char* layout_name = DOS_GetLoadedLayout();
|
|
int32_t cp = dos.loaded_codepage;
|
|
Bitu keyb_error;
|
|
if(n == 1) {
|
|
if(newCP == 932 || newCP == 936 || newCP == 949 || newCP == 950 || newCP == 951
|
|
#if defined(USE_TTF)
|
|
|| (ttf.inUse && (newCP >= 1250 && newCP <= 1258))
|
|
#endif
|
|
) {
|
|
missing = toSetCodePage(this, newCP, -1);
|
|
if(missing > -1) SwitchLanguage(cp, newCP, true);
|
|
if(missing > 0) WriteOut(MSG_Get("SHELL_CMD_CHCP_MISSING"), missing);
|
|
}
|
|
else {
|
|
#if defined(USE_TTF)
|
|
if(ttf.inUse && !isSupportedCP(newCP)) {
|
|
WriteOut(MSG_Get("SHELL_CMD_CHCP_INVALID"), StripArg(args));
|
|
LOG_MSG("CHCP: Codepage %d not supported for TTF output", newCP);
|
|
return;
|
|
}
|
|
#endif
|
|
keyb_error = DOS_ChangeCodepage(newCP, "auto");
|
|
if(keyb_error == KEYB_NOERROR) {
|
|
SwitchLanguage(cp, newCP, true);
|
|
if(layout_name != NULL) {
|
|
keyb_error = DOS_ChangeKeyboardLayout(layout_name, cp);
|
|
}
|
|
}
|
|
else
|
|
WriteOut(MSG_Get("SHELL_CMD_CHCP_INVALID"), StripArg(args));
|
|
}
|
|
WriteOut(MSG_Get("SHELL_CMD_CHCP_ACTIVE"), dos.loaded_codepage);
|
|
}
|
|
else if(n == 2 && strlen(buff)) {
|
|
if(*buff == ':' && strchr(StripArg(args), ':')) {
|
|
std::string name = buff + 1;
|
|
if(name.empty() && iter != langcp_map.end()) name = iter->second;
|
|
if(newCP == 932 || newCP == 936 || newCP == 949 || newCP == 950 || newCP == 951) {
|
|
missing = toSetCodePage(this, newCP, -1);
|
|
if(missing > -1) SwitchLanguage(cp, newCP, true);
|
|
if(missing > 0) WriteOut(MSG_Get("SHELL_CMD_CHCP_MISSING"), missing);
|
|
}
|
|
#if defined(USE_TTF)
|
|
else if(ttf.inUse) {
|
|
if(newCP >= 1250 && newCP <= 1258) {
|
|
missing = toSetCodePage(this, newCP, -1);
|
|
if(missing > -1) SwitchLanguage(cp, newCP, true);
|
|
if(missing > 0) WriteOut(MSG_Get("SHELL_CMD_CHCP_MISSING"), missing);
|
|
}
|
|
else if(!isSupportedCP(newCP)) {
|
|
WriteOut(MSG_Get("SHELL_CMD_CHCP_INVALID"), StripArg(args));
|
|
LOG_MSG("CHCP: Codepage %d not supported for TTF output", newCP);
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
else {
|
|
keyb_error = DOS_ChangeCodepage(newCP, "auto");
|
|
if(keyb_error == KEYB_NOERROR) {
|
|
if(layout_name != NULL) {
|
|
keyb_error = DOS_ChangeKeyboardLayout(layout_name, cp);
|
|
}
|
|
}
|
|
else
|
|
WriteOut(MSG_Get("SHELL_CMD_CHCP_INVALID"), StripArg(args));
|
|
}
|
|
if(name.size() && dos.loaded_codepage == newCP) {
|
|
SetVal("dosbox", "language", name);
|
|
Load_Language(name);
|
|
}
|
|
WriteOut(MSG_Get("SHELL_CMD_CHCP_ACTIVE"), dos.loaded_codepage);
|
|
return;
|
|
}
|
|
#if defined(USE_TTF)
|
|
if(ttf.inUse) {
|
|
if(isSupportedCP(newCP)) {
|
|
missing = toSetCodePage(this, newCP, -1);
|
|
if(missing > -1) SwitchLanguage(cp, newCP, true);
|
|
if(missing > 0) WriteOut(MSG_Get("SHELL_CMD_CHCP_MISSING"), missing);
|
|
LOG_MSG("CHCP: Loading cpi/cpx files ignored for TTF output");
|
|
}
|
|
else {
|
|
WriteOut(MSG_Get("SHELL_CMD_CHCP_INVALID"), StripArg(args));
|
|
LOG_MSG("CHCP: Codepage %d not supported for TTF output", newCP);
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
#endif
|
|
altcp = 0;
|
|
for(int i = 0; i < 256; i++) altcp_to_unicode[i] = 0;
|
|
std::string cpfile = buff;
|
|
FILE* file = fopen(cpfile.c_str(), "r"); /* should check the result */
|
|
std::string exepath = GetDOSBoxXPath();
|
|
if(!file && exepath.size()) file = fopen((exepath + CROSS_FILESPLIT + cpfile).c_str(), "r");
|
|
if(file && newCP > 0 && newCP != 932 && newCP != 936 && newCP != 949 && newCP != 950 && newCP != 951) {
|
|
altcp = newCP;
|
|
char line[256], * l = line;
|
|
while(fgets(line, sizeof(line), file)) {
|
|
l = trim(l);
|
|
if(!strlen(l)) continue;
|
|
r = strchr(l, '#');
|
|
if(r) *r = 0;
|
|
l = trim(l);
|
|
if(!strlen(l) || strncasecmp(l, "0x", 2)) continue;
|
|
r = strchr(l, ' ');
|
|
if(!r) r = strchr(l, '\t');
|
|
if(!r) continue;
|
|
*r = 0;
|
|
int ind = (int)strtol(l + 2, NULL, 16);
|
|
r = trim(r + 1);
|
|
if(ind > 0xFF || strncasecmp(r, "0x", 2)) continue;
|
|
int map = (int)strtol(r + 2, NULL, 16);
|
|
altcp_to_unicode[ind] = map;
|
|
}
|
|
if(file) fclose(file);
|
|
keyb_error = DOS_ChangeCodepage(newCP, cpfile.c_str());
|
|
if(keyb_error == KEYB_NOERROR) {
|
|
if(layout_name != NULL) {
|
|
keyb_error = DOS_ChangeKeyboardLayout(layout_name, cp);
|
|
}
|
|
}
|
|
WriteOut(MSG_Get("SHELL_CMD_CHCP_ACTIVE"), dos.loaded_codepage);
|
|
#if defined(USE_TTF)
|
|
if(missing > 0) WriteOut(MSG_Get("SHELL_CMD_CHCP_MISSING"), missing);
|
|
#endif
|
|
}
|
|
else
|
|
WriteOut(MSG_Get("SHELL_CMD_CHCP_INVALID"), StripArg(args));
|
|
if(file) fclose(file);
|
|
#if defined(USE_TTF)
|
|
}
|
|
#endif
|
|
}
|
|
else WriteOut(MSG_Get("SHELL_CMD_CHCP_INVALID"), StripArg(args));
|
|
return;
|
|
}
|
|
|
|
void DOS_Shell::CMD_VTEXT(char *args)
|
|
{
|
|
HELP("VTEXT");
|
|
if (!IS_DOSV) {
|
|
WriteOut("This command is only supported in DOS/V mode.\n");
|
|
return;
|
|
}
|
|
if (char* rem = ScanCMDRemain(args)) {
|
|
WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"), rem);
|
|
return;
|
|
}
|
|
args = trim(args);
|
|
if(args && *args) {
|
|
uint8_t new_mode = 0xff;
|
|
char *word = StripWord(args);
|
|
if(!strcasecmp(word, "1"))
|
|
new_mode = 0x70;
|
|
else if(!strcasecmp(word, "2"))
|
|
new_mode = 0x78;
|
|
else if(!strcasecmp(word, "0"))
|
|
new_mode = 0x03;
|
|
else {
|
|
WriteOut(MSG_Get("SHELL_INVALID_PARAMETER"), word);
|
|
return;
|
|
}
|
|
if(new_mode != 0xff) {
|
|
uint16_t oldax=reg_ax;
|
|
reg_ax = new_mode;
|
|
CALLBACK_RunRealInt(0x10);
|
|
if(new_mode == 0x78) new_mode = 0x70;
|
|
reg_ax = oldax;
|
|
}
|
|
}
|
|
uint8_t mode = real_readb(BIOSMEM_SEG, BIOSMEM_CURRENT_MODE);
|
|
WriteOut(MSG_Get(mode == 0x70?"SHELL_CMD_VTEXT_ON":"SHELL_CMD_VTEXT_OFF"));
|
|
}
|