dosbox-wii/src/shell/shell_misc.cpp

556 lines
15 KiB
C++
Raw Normal View History

2009-05-02 23:03:37 +02:00
/*
2009-05-03 00:18:08 +02:00
* Copyright (C) 2002-2006 The DOSBox Team
2009-05-02 23:03:37 +02:00
*
* 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
2009-05-03 00:02:15 +02:00
* GNU General Public License for more details.
2009-05-02 23:03:37 +02:00
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
2009-05-03 00:18:08 +02:00
/* $Id: shell_misc.cpp,v 1.43 2006/02/28 19:41:27 qbix79 Exp $ */
2009-05-02 23:43:00 +02:00
2009-05-02 23:03:37 +02:00
#include <stdlib.h>
#include <string.h>
2009-05-03 00:08:43 +02:00
#include <algorithm> //std::copy
#include <iterator> //std::front_inserter
2009-05-02 23:53:27 +02:00
#include "shell.h"
2009-05-02 23:43:00 +02:00
#include "regs.h"
2009-05-03 00:18:08 +02:00
#include "callback.h"
#include "support.h"
2009-05-02 23:03:37 +02:00
void DOS_Shell::ShowPrompt(void) {
Bit8u drive=DOS_GetDefaultDrive()+'A';
2009-05-02 23:12:18 +02:00
char dir[DOS_PATHLENGTH];
2009-05-02 23:03:37 +02:00
DOS_GetCurrentDir(0,dir);
WriteOut("%c:\\%s>",drive,dir);
}
static void outc(Bit8u c) {
Bit16u n=1;
DOS_WriteFile(STDOUT,&c,&n);
}
static void outs(char * str) {
Bit16u n=strlen(str);
DOS_WriteFile(STDOUT,(Bit8u *)str,&n);
}
void DOS_Shell::InputCommand(char * line) {
2009-05-03 00:02:15 +02:00
Bitu size=CMD_MAXLINE-2; //lastcharacter+0
2009-05-02 23:03:37 +02:00
Bit8u c;Bit16u n=1;
Bitu str_len=0;Bitu str_index=0;
2009-05-02 23:43:00 +02:00
Bit16u len;
line[0] = '\0';
std::list<std::string>::iterator it_history = l_history.begin(), it_completion = l_completion.begin();
2009-05-02 23:03:37 +02:00
while (size) {
2009-05-03 00:02:15 +02:00
dos.echo=false;
2009-05-03 00:18:08 +02:00
while(!DOS_ReadFile(input_handle,&c,&n)) {
Bit16u dummy;
DOS_CloseFile(input_handle);
DOS_OpenFile("con",2,&dummy);
LOG(LOG_MISC,LOG_ERROR)("Reopening the input handle.This is a bug!");
}
2009-05-02 23:03:37 +02:00
if (!n) {
size=0; //Kill the while loop
continue;
}
switch (c) {
case 0x00: /* Extended Keys */
{
DOS_ReadFile(input_handle,&c,&n);
switch (c) {
2009-05-02 23:43:00 +02:00
2009-05-02 23:03:37 +02:00
case 0x3d: /* F3 */
2009-05-02 23:43:00 +02:00
if (!l_history.size()) break;
it_history = l_history.begin();
if (it_history != l_history.end() && it_history->length() > str_len) {
const char *reader = &(it_history->c_str())[str_len];
while ((c = *reader++)) {
line[str_index ++] = c;
2009-05-02 23:03:37 +02:00
DOS_WriteFile(STDOUT,&c,&n);
}
2009-05-02 23:43:00 +02:00
str_len = str_index = it_history->length();
2009-05-03 00:02:15 +02:00
size = CMD_MAXLINE - str_index - 2;
2009-05-03 00:18:08 +02:00
line[str_len] = 0;
2009-05-02 23:03:37 +02:00
}
break;
2009-05-02 23:43:00 +02:00
case 0x4B: /* LEFT */
if (str_index) {
outc(8);
str_index --;
}
2009-05-02 23:03:37 +02:00
break;
2009-05-02 23:43:00 +02:00
case 0x4D: /* RIGHT */
if (str_index < str_len) {
outc(line[str_index++]);
}
break;
2009-05-03 00:18:08 +02:00
case 0x47: /* HOME */
while (str_index) {
outc(8);
str_index--;
}
break;
case 0x4F: /* END */
while (str_index < str_len) {
outc(line[str_index++]);
}
break;
2009-05-02 23:43:00 +02:00
case 0x48: /* UP */
if (l_history.empty() || it_history == l_history.end()) break;
for (;str_index>0; str_index--) {
// removes all characters
outc(8); outc(' '); outc(8);
}
strcpy(line, it_history->c_str());
len = it_history->length();
str_len = str_index = len;
2009-05-03 00:02:15 +02:00
size = CMD_MAXLINE - str_index - 2;
2009-05-02 23:43:00 +02:00
DOS_WriteFile(STDOUT, (Bit8u *)line, &len);
it_history ++;
break;
case 0x50: /* DOWN */
if (l_history.empty() || it_history == l_history.begin()) break;
// not very nice but works ..
it_history --;
if (it_history == l_history.begin()) {
// no previous commands in history
it_history ++;
break;
} else it_history --;
for (;str_index>0; str_index--) {
// removes all characters
outc(8); outc(' '); outc(8);
}
strcpy(line, it_history->c_str());
len = it_history->length();
str_len = str_index = len;
2009-05-03 00:02:15 +02:00
size = CMD_MAXLINE - str_index - 2;
2009-05-02 23:43:00 +02:00
DOS_WriteFile(STDOUT, (Bit8u *)line, &len);
it_history ++;
break;
2009-05-03 00:08:43 +02:00
case 0x53:/* DELETE */
{
if(str_index>=str_len) break;
Bit16u a=str_len-str_index-1;
Bit8u* text=reinterpret_cast<Bit8u*>(&line[str_index+1]);
DOS_WriteFile(STDOUT,text,&a);//write buffer to screen
outc(' ');outc(8);
for(Bitu i=str_index;i<str_len-1;i++) {
line[i]=line[i+1];
outc(8);
}
line[--str_len]=0;
size++;
}
break;
2009-05-02 23:43:00 +02:00
default:
break;
2009-05-02 23:03:37 +02:00
}
};
break;
case 0x08: /* BackSpace */
2009-05-02 23:43:00 +02:00
if (str_index) {
outc(8);
Bit32u str_remain=str_len - str_index;
2009-05-03 00:02:15 +02:00
size++;
2009-05-02 23:03:37 +02:00
if (str_remain) {
2009-05-02 23:43:00 +02:00
memmove(&line[str_index-1],&line[str_index],str_remain);
line[--str_len]=0;
str_index --;
2009-05-02 23:03:37 +02:00
/* Go back to redraw */
2009-05-02 23:43:00 +02:00
for (Bit16u i=str_index; i < str_len; i++)
outc(line[i]);
} else {
line[--str_index] = '\0';
str_len--;
2009-05-02 23:03:37 +02:00
}
2009-05-02 23:43:00 +02:00
outc(' '); outc(8);
// moves the cursor left
while (str_remain--) outc(8);
2009-05-02 23:03:37 +02:00
}
2009-05-03 00:18:08 +02:00
if (l_completion.size()) l_completion.clear();
2009-05-02 23:03:37 +02:00
break;
case 0x0a: /* New Line not handled */
/* Don't care */
break;
case 0x0d: /* Return */
outc('\n');
size=0; //Kill the while loop
break;
2009-05-02 23:43:00 +02:00
case'\t':
{
if (l_completion.size()) {
it_completion ++;
if (it_completion == l_completion.end()) it_completion = l_completion.begin();
} else {
// build new completion list
// get completion mask
char *completion_start = strrchr(line, ' ');
if (completion_start) {
completion_start ++;
completion_index = str_index - strlen(completion_start);
} else {
completion_start = line;
completion_index = 0;
}
2009-05-03 00:02:15 +02:00
char *path;
if ((path = strrchr(line+completion_index,'\\'))) completion_index = path-line+1;
if ((path = strrchr(line+completion_index,'/'))) completion_index = path-line+1;
2009-05-02 23:43:00 +02:00
// build the completion list
char mask[DOS_PATHLENGTH];
if (completion_start) {
strcpy(mask, completion_start);
2009-05-03 00:18:08 +02:00
char* dot_pos=strrchr(mask,'.');
char* bs_pos=strrchr(mask,'\\');
char* fs_pos=strrchr(mask,'/');
char* cl_pos=strrchr(mask,':');
2009-05-02 23:43:00 +02:00
// not perfect when line already contains wildcards, but works
2009-05-03 00:18:08 +02:00
if ((dot_pos-bs_pos>0) && (dot_pos-fs_pos>0) && (dot_pos-cl_pos>0))
strcat(mask, "*");
else strcat(mask, "*.*");
2009-05-02 23:43:00 +02:00
} else {
strcpy(mask, "*.*");
}
2009-05-03 00:18:08 +02:00
RealPt save_dta=dos.dta();
dos.dta(dos.tables.tempdta);
2009-05-02 23:43:00 +02:00
bool res = DOS_FindFirst(mask, 0xffff & ~DOS_ATTR_VOLUME);
2009-05-03 00:18:08 +02:00
if (!res) {
dos.dta(save_dta);
break; // TODO: beep
}
2009-05-02 23:43:00 +02:00
2009-05-03 00:02:15 +02:00
DOS_DTA dta(dos.dta());
2009-05-02 23:43:00 +02:00
char name[DOS_NAMELENGTH_ASCII];Bit32u size;Bit16u date;Bit16u time;Bit8u attr;
2009-05-03 00:08:43 +02:00
std::list<std::string> executable;
2009-05-02 23:43:00 +02:00
while (res) {
dta.GetResult(name,size,date,time,attr);
// add result to completion list
char *ext; // file extension
if (strcmp(name, ".") && strcmp(name, "..")) {
ext = strrchr(name, '.');
if (ext && (strcmp(ext, ".BAT") == 0 || strcmp(ext, ".COM") == 0 || strcmp(ext, ".EXE") == 0))
2009-05-03 00:08:43 +02:00
// we add executables to the a seperate list and place that list infront of the normal files
executable.push_front(name);
2009-05-02 23:43:00 +02:00
else
l_completion.push_back(name);
}
res=DOS_FindNext();
}
2009-05-03 00:08:43 +02:00
/* Add excutable list to front of completion list. */
std::copy(executable.begin(),executable.end(),std::front_inserter(l_completion));
2009-05-02 23:43:00 +02:00
it_completion = l_completion.begin();
2009-05-03 00:18:08 +02:00
dos.dta(save_dta);
2009-05-02 23:43:00 +02:00
}
if (l_completion.size() && it_completion->length()) {
for (;str_index > completion_index; str_index--) {
// removes all characters
outc(8); outc(' '); outc(8);
}
strcpy(&line[completion_index], it_completion->c_str());
len = it_completion->length();
str_len = str_index = completion_index + len;
2009-05-03 00:02:15 +02:00
size = CMD_MAXLINE - str_index - 2;
2009-05-02 23:43:00 +02:00
DOS_WriteFile(STDOUT, (Bit8u *)it_completion->c_str(), &len);
}
}
break;
case 0x1b: /* ESC */
//write a backslash and return to the next line
outc('\\');
outc('\n');
*line = 0; // reset the line.
if (l_completion.size()) l_completion.clear(); //reset the completion list.
this->InputCommand(line); //Get the NEW line.
size = 0; // stop the next loop
str_len = 0; // prevent multiple adds of the same line
break;
2009-05-02 23:03:37 +02:00
default:
2009-05-02 23:43:00 +02:00
if (l_completion.size()) l_completion.clear();
2009-05-03 00:08:43 +02:00
if(str_index < str_len && true) { //mem_readb(BIOS_KEYBOARD_FLAGS1)&0x80) dev_con.h ?
outc(' ');//move cursor one to the right.
Bit16u a = str_len - str_index;
Bit8u* text=reinterpret_cast<Bit8u*>(&line[str_index]);
DOS_WriteFile(STDOUT,text,&a);//write buffer to screen
outc(8);//undo the cursor the right.
for(Bitu i=str_len;i>str_index;i--) {
line[i]=line[i-1]; //move internal buffer
outc(8); //move cursor back (from write buffer to screen)
}
line[++str_len]=0;//new end (as the internal buffer moved one place to the right
size--;
};
2009-05-02 23:03:37 +02:00
line[str_index]=c;
2009-05-02 23:43:00 +02:00
str_index ++;
2009-05-03 00:02:15 +02:00
if (str_index > str_len){
line[str_index] = '\0';
2009-05-03 00:08:43 +02:00
str_len++;
2009-05-03 00:02:15 +02:00
size--;
}
2009-05-02 23:03:37 +02:00
DOS_WriteFile(STDOUT,&c,&n);
break;
}
}
2009-05-02 23:43:00 +02:00
2009-05-02 23:03:37 +02:00
if (!str_len) return;
str_len++;
2009-05-02 23:43:00 +02:00
// add command line to history
l_history.push_front(line); it_history = l_history.begin();
if (l_completion.size()) l_completion.clear();
2009-05-02 23:03:37 +02:00
}
2009-05-03 00:18:08 +02:00
bool DOS_Shell::Execute(char * name,char * args) {
/* return true => don't check for hardware changes in do_command
* return false => check for hardware changes in do_command */
2009-05-02 23:03:37 +02:00
char * fullname;
2009-05-03 00:02:15 +02:00
char line[CMD_MAXLINE];
2009-05-02 23:43:00 +02:00
if(strlen(args)!= 0){
if(*args != ' '){ //put a space in front
line[0]=' ';line[1]=0;
2009-05-03 00:18:08 +02:00
strncat(line,args,CMD_MAXLINE-2);
line[CMD_MAXLINE-1]=0;
2009-05-02 23:43:00 +02:00
}
else
{
2009-05-03 00:18:08 +02:00
safe_strncpy(line,args,CMD_MAXLINE);
2009-05-02 23:43:00 +02:00
}
}else{
line[0]=0;
};
2009-05-02 23:03:37 +02:00
/* check for a drive change */
if ((strcmp(name + 1, ":") == 0) && isalpha(*name))
{
if (!DOS_SetDrive(toupper(name[0])-'A')) {
WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_NOT_FOUND"),toupper(name[0]));
}
2009-05-03 00:18:08 +02:00
return true;
2009-05-02 23:03:37 +02:00
}
/* Check for a full name */
fullname=Which(name);
2009-05-03 00:18:08 +02:00
if (!fullname) return false;
2009-05-02 23:43:00 +02:00
char* extension =strrchr(fullname,'.');
/*always disallow files without extension from being executed. */
/*only internal commands can be run this way and they never get in this handler */
if(extension == 0)
{
2009-05-03 00:18:08 +02:00
char temp_name[DOS_PATHLENGTH+4],* temp_fullname;
2009-05-02 23:43:00 +02:00
//try to add .com, .exe and .bat extensions to filename
strcpy(temp_name,fullname);
strcat(temp_name,".COM");
temp_fullname=Which(temp_name);
if (temp_fullname) { extension=".com";strcpy(fullname,temp_fullname); }
else
{
strcpy(temp_name,fullname);
strcat(temp_name,".EXE");
temp_fullname=Which(temp_name);
if (temp_fullname) { extension=".exe";strcpy(fullname,temp_fullname);}
else
{
strcpy(temp_name,fullname);
strcat(temp_name,".BAT");
temp_fullname=Which(temp_name);
if (temp_fullname) { extension=".bat";strcpy(fullname,temp_fullname);}
else
{
2009-05-03 00:18:08 +02:00
return false;
2009-05-02 23:43:00 +02:00
}
}
}
}
if (strcasecmp(extension, ".bat") == 0)
{ /* Run the .bat file */
/* delete old batch file if call is not active*/
bool temp_echo=echo; /*keep the current echostate (as delete bf might change it )*/
if(bf && !call) delete bf;
2009-05-02 23:20:05 +02:00
bf=new BatchFile(this,fullname,line);
2009-05-02 23:43:00 +02:00
echo=temp_echo; //restore it.
}
else
{ /* only .bat .exe .com extensions maybe be executed by the shell */
if(strcasecmp(extension, ".com") !=0)
{
2009-05-03 00:18:08 +02:00
if(strcasecmp(extension, ".exe") !=0) return false;
2009-05-02 23:43:00 +02:00
}
2009-05-02 23:12:18 +02:00
/* Run the .exe or .com file from the shell */
/* Allocate some stack space for tables in physical memory */
reg_sp-=0x200;
//Add Parameter block
DOS_ParamBlock block(SegPhys(ss)+reg_sp);
2009-05-02 23:20:05 +02:00
block.Clear();
2009-05-02 23:12:18 +02:00
//Add a filename
RealPt file_name=RealMakeSeg(ss,reg_sp+0x20);
MEM_BlockWrite(Real2Phys(file_name),fullname,strlen(fullname)+1);
2009-05-02 23:03:37 +02:00
/* Fill the command line */
CommandTail cmd;
2009-05-02 23:20:05 +02:00
if (strlen(line)>126) line[126]=0;
cmd.count=strlen(line);
memcpy(cmd.buffer,line,strlen(line));
cmd.buffer[strlen(line)]=0xd;
/* Copy command line in stack block too */
MEM_BlockWrite(SegPhys(ss)+reg_sp+0x100,&cmd,128);
2009-05-02 23:27:47 +02:00
/* Parse FCB (first two parameters) and put them into the current DOS_PSP */
Bit8u add;
2009-05-03 00:02:15 +02:00
FCB_Parsename(dos.psp(),0x5C,0x00,cmd.buffer,&add);
FCB_Parsename(dos.psp(),0x6C,0x00,&cmd.buffer[add],&add);
block.exec.fcb1=RealMake(dos.psp(),0x5C);
block.exec.fcb2=RealMake(dos.psp(),0x6C);
2009-05-02 23:20:05 +02:00
/* Set the command line in the block and save it */
block.exec.cmdtail=RealMakeSeg(ss,reg_sp+0x100);
block.SaveData();
2009-05-02 23:43:00 +02:00
#if 0
2009-05-02 23:03:37 +02:00
/* Save CS:IP to some point where i can return them from */
2009-05-02 23:27:47 +02:00
Bit32u oldeip=reg_eip;
Bit16u oldcs=SegValue(cs);
2009-05-02 23:20:05 +02:00
RealPt newcsip=CALLBACK_RealPointer(call_shellstop);
2009-05-02 23:12:18 +02:00
SegSet16(cs,RealSeg(newcsip));
2009-05-02 23:03:37 +02:00
reg_ip=RealOff(newcsip);
2009-05-02 23:43:00 +02:00
#endif
2009-05-02 23:12:18 +02:00
/* Start up a dos execute interrupt */
reg_ax=0x4b00;
//Filename pointer
SegSet16(ds,SegValue(ss));
reg_dx=RealOff(file_name);
//Paramblock
SegSet16(es,SegValue(ss));
reg_bx=reg_sp;
2009-05-02 23:43:00 +02:00
SETFLAGBIT(IF,false);
2009-05-02 23:12:18 +02:00
CALLBACK_RunRealInt(0x21);
2009-05-02 23:27:47 +02:00
/* Restore CS:IP and the stack */
2009-05-02 23:12:18 +02:00
reg_sp+=0x200;
2009-05-02 23:43:00 +02:00
#if 0
2009-05-02 23:27:47 +02:00
reg_eip=oldeip;
SegSet16(cs,oldcs);
2009-05-02 23:43:00 +02:00
#endif
2009-05-02 23:03:37 +02:00
}
2009-05-03 00:18:08 +02:00
return true; //Executable started
2009-05-02 23:03:37 +02:00
}
static char * bat_ext=".BAT";
static char * com_ext=".COM";
static char * exe_ext=".EXE";
2009-05-03 00:02:15 +02:00
static char which_ret[DOS_PATHLENGTH+4];
2009-05-02 23:03:37 +02:00
char * DOS_Shell::Which(char * name) {
2009-05-03 00:18:08 +02:00
size_t name_len = strlen(name);
if(name_len >= DOS_PATHLENGTH) return 0;
2009-05-03 00:02:15 +02:00
2009-05-02 23:03:37 +02:00
/* Parse through the Path to find the correct entry */
/* Check if name is already ok but just misses an extension */
2009-05-03 00:02:15 +02:00
2009-05-02 23:43:00 +02:00
if (DOS_FileExists(name)) return name;
/* try to find .com .exe .bat */
strcpy(which_ret,name);
strcat(which_ret,com_ext);
if (DOS_FileExists(which_ret)) return which_ret;
strcpy(which_ret,name);
strcat(which_ret,exe_ext);
if (DOS_FileExists(which_ret)) return which_ret;
strcpy(which_ret,name);
strcat(which_ret,bat_ext);
if (DOS_FileExists(which_ret)) return which_ret;
2009-05-02 23:03:37 +02:00
2009-05-02 23:20:05 +02:00
/* No Path in filename look through path environment string */
2009-05-02 23:43:00 +02:00
char path[DOS_PATHLENGTH];std::string temp;
2009-05-02 23:20:05 +02:00
if (!GetEnvStr("PATH",temp)) return 0;
const char * pathenv=temp.c_str();
2009-05-02 23:03:37 +02:00
if (!pathenv) return 0;
pathenv=strchr(pathenv,'=');
if (!pathenv) return 0;
pathenv++;
2009-05-03 00:18:08 +02:00
Bitu i_path = 0;
2009-05-02 23:03:37 +02:00
while (*pathenv) {
2009-05-02 23:43:00 +02:00
/* remove ; and ;; at the beginning. (and from the second entry etc) */
while(*pathenv && (*pathenv ==';'))
pathenv++;
/* get next entry */
2009-05-03 00:18:08 +02:00
i_path = 0; /* reset writer */
while(*pathenv && (*pathenv !=';') && (i_path < DOS_PATHLENGTH) )
path[i_path++] = *pathenv++;
if(i_path == DOS_PATHLENGTH) {
/* If max size. move till next ; and terminate path */
while(*pathenv != ';')
pathenv++;
path[DOS_PATHLENGTH - 1] = 0;
} else path[i_path] = 0;
2009-05-02 23:43:00 +02:00
/* check entry */
2009-05-03 00:18:08 +02:00
if(size_t len = strlen(path)){
if(len >= (DOS_PATHLENGTH - 2)) continue;
if(path[len - 1] != '\\') {
strcat(path,"\\");
len++;
}
2009-05-03 00:02:15 +02:00
//If name too long =>next
2009-05-03 00:18:08 +02:00
if((name_len + len + 1) >= DOS_PATHLENGTH) continue;
strcat(path,name);
2009-05-03 00:02:15 +02:00
2009-05-02 23:03:37 +02:00
strcpy(which_ret,path);
2009-05-02 23:43:00 +02:00
if (DOS_FileExists(which_ret)) return which_ret;
strcpy(which_ret,path);
strcat(which_ret,com_ext);
if (DOS_FileExists(which_ret)) return which_ret;
strcpy(which_ret,path);
strcat(which_ret,exe_ext);
if (DOS_FileExists(which_ret)) return which_ret;
strcpy(which_ret,path);
strcat(which_ret,bat_ext);
if (DOS_FileExists(which_ret)) return which_ret;
2009-05-02 23:03:37 +02:00
}
}
return 0;
}