/* * Copyright (C) 2002-2004 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* $Id: shell_cmds.cpp,v 1.47 2004/09/18 10:20:54 qbix79 Exp $ */ #include #include #include "shell.h" #include "callback.h" #include "regs.h" #include "../dos/drives.h" static SHELL_Cmd cmd_list[]={ { "CHDIR", 0, &DOS_Shell::CMD_CHDIR, "SHELL_CMD_CHDIR_HELP"}, { "CD", 1, &DOS_Shell::CMD_CHDIR, "SHELL_CMD_CHDIR_HELP"}, { "CLS", 0, &DOS_Shell::CMD_CLS, "SHELL_CMD_CLS_HELP"}, { "COPY", 0, &DOS_Shell::CMD_COPY, "SHELL_CMD_COPY_HELP"}, { "DIR", 0, &DOS_Shell::CMD_DIR, "SHELL_CMD_DIR_HELP"}, { "DEL", 1, &DOS_Shell::CMD_DELETE, "SHELL_CMD_DELETE_HELP"}, { "DELETE", 0, &DOS_Shell::CMD_DELETE, "SHELL_CMD_DELETE_HELP"}, { "ERASE", 1, &DOS_Shell::CMD_DELETE, "SHELL_CMD_DELETE_HELP"}, { "ECHO", 0, &DOS_Shell::CMD_ECHO, "SHELL_CMD_ECHO_HELP"}, { "EXIT", 0, &DOS_Shell::CMD_EXIT, "SHELL_CMD_EXIT_HELP"}, { "HELP", 1, &DOS_Shell::CMD_HELP, "SHELL_CMD_HELP_HELP"}, { "MKDIR", 0, &DOS_Shell::CMD_MKDIR, "SHELL_CMD_MKDIR_HELP"}, { "MD", 1, &DOS_Shell::CMD_MKDIR, "SHELL_CMD_MKDIR_HELP"}, { "RMDIR", 0, &DOS_Shell::CMD_RMDIR, "SHELL_CMD_RMDIR_HELP"}, { "RD", 1, &DOS_Shell::CMD_RMDIR, "SHELL_CMD_RMDIR_HELP"}, { "SET", 0, &DOS_Shell::CMD_SET, "SHELL_CMD_SET_HELP"}, { "IF", 0, &DOS_Shell::CMD_IF, "SHELL_CMD_IF_HELP"}, { "GOTO", 0, &DOS_Shell::CMD_GOTO, "SHELL_CMD_GOTO_HELP"}, { "TYPE", 0, &DOS_Shell::CMD_TYPE, "SHELL_CMD_TYPE_HELP"}, { "REM", 0, &DOS_Shell::CMD_REM, "SHELL_CMD_REM_HELP"}, { "RENAME", 0, &DOS_Shell::CMD_RENAME, "SHELL_CMD_RENAME_HELP"}, { "REN", 1, &DOS_Shell::CMD_RENAME, "SHELL_CMD_RENAME_HELP"}, { "PAUSE", 0, &DOS_Shell::CMD_PAUSE, "SHELL_CMD_PAUSE_HELP"}, { "CALL", 0, &DOS_Shell::CMD_CALL, "SHELL_CMD_CALL_HELP"}, { "SUBST", 0, &DOS_Shell::CMD_SUBST, "SHELL_CMD_SUBST_HELP"}, { "LOADHIGH", 0, &DOS_Shell::CMD_LOADHIGH, "SHELL_CMD_LOADHIGH_HELP"}, { "LH", 1, &DOS_Shell::CMD_LOADHIGH, "SHELL_CMD_LOADHIGH_HELP"}, { "CHOICE", 0, &DOS_Shell::CMD_CHOICE, "SHELL_CMD_CHOICE_HELP"}, { "ATTRIB", 0, &DOS_Shell::CMD_ATTRIB, "SHELL_CMD_ATTRIB_HELP"}, {0,0,0,0} }; void DOS_Shell::DoCommand(char * line) { /* First split the line into command and arguments */ line=trim(line); char cmd[CMD_MAXLINE]; char * cmd_write=cmd; while (*line) { if (*line==32) break; if (*line=='/') break; if (*line=='\t') break; if ((*line=='.') ||(*line =='\\')) { //allow stuff like cd.. and dir.exe cd\kees *cmd_write=0; Bit32u cmd_index=0; while (cmd_list[cmd_index].name) { if (strcasecmp(cmd_list[cmd_index].name,cmd)==0) { (this->*(cmd_list[cmd_index].handler))(line); return; } cmd_index++; } } *cmd_write++=*line++; } *cmd_write=0; if (strlen(cmd)==0) return; /* Check the internal list */ Bit32u cmd_index=0; while (cmd_list[cmd_index].name) { if (strcasecmp(cmd_list[cmd_index].name,cmd)==0) { (this->*(cmd_list[cmd_index].handler))(line); return; } cmd_index++; } /* This isn't an internal command execute it */ Execute(cmd,line); } void DOS_Shell::CMD_CLS(char * args) { reg_ax=0x0003; CALLBACK_RunRealInt(0x10); }; void DOS_Shell::CMD_DELETE(char * args) { char * rem=ScanCMDRemain(args); if (rem) { WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem); return; } /* If delete accept switches mind the space infront of them. See the dir /p code */ char full[DOS_PATHLENGTH]; char buffer[CROSS_LEN]; args = ExpandDot(args,buffer); StripSpaces(args); if (!DOS_Canonicalize(args,full)) { WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));return; } //TODO Maybe support confirmation for *.* like dos does. bool res=DOS_FindFirst(args,0xffff & ~DOS_ATTR_VOLUME); if (!res) { WriteOut(MSG_Get("SHELL_CMD_DEL_ERROR"),args);return; } //end can't be 0, but if it is we'll get a nice crash, who cares :) char * end=strrchr(full,'\\')+1;*end=0; char name[DOS_NAMELENGTH_ASCII];Bit32u size;Bit16u time,date;Bit8u attr; DOS_DTA dta(dos.dta()); while (res) { dta.GetResult(name,size,date,time,attr); if (!(attr & (DOS_ATTR_DIRECTORY|DOS_ATTR_READ_ONLY))) { strcpy(end,name); if (!DOS_UnlinkFile(full)) WriteOut(MSG_Get("SHELL_CMD_DEL_ERROR"),full); } res=DOS_FindNext(); } } void DOS_Shell::CMD_HELP(char * args){ /* Print the help */ WriteOut(MSG_Get("SHELL_CMD_HELP")); Bit32u cmd_index=0; while (cmd_list[cmd_index].name) { if (!cmd_list[cmd_index].flags) WriteOut("%-8s %s",cmd_list[cmd_index].name,MSG_Get(cmd_list[cmd_index].help)); cmd_index++; } } void DOS_Shell::CMD_RENAME(char * args){ StripSpaces(args); if(!*args) {SyntaxError();return;} if((strchr(args,'*')!=NULL) || (strchr(args,'?')!=NULL) ) { WriteOut(MSG_Get("SHELL_CMD_NO_WILD"));return;} char * arg1=StripWord(args); DOS_Rename(arg1,args); } 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; strcpy(buffer,args); StripSpaces(pbuffer); if (strcasecmp(pbuffer,"OFF")==0) { echo=false; return; } if (strcasecmp(pbuffer,"ON")==0) { echo=true; return; } args++;//skip first character. either a slash or dot or space WriteOut("%s\n",args); }; void DOS_Shell::CMD_EXIT(char * args) { exit=true; }; void DOS_Shell::CMD_CHDIR(char * args) { StripSpaces(args); if (!*args) { Bit8u drive=DOS_GetDefaultDrive()+'A'; char dir[DOS_PATHLENGTH]; DOS_GetCurrentDir(0,dir); WriteOut("%c:\\%s\n",drive,dir); } else if (!DOS_ChangeDir(args)) { WriteOut(MSG_Get("SHELL_CMD_CHDIR_ERROR"),args); } }; void DOS_Shell::CMD_MKDIR(char * args) { StripSpaces(args); char * rem=ScanCMDRemain(args); if (rem) { WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem); return; } if (!DOS_MakeDir(args)) { WriteOut(MSG_Get("SHELL_CMD_MKDIR_ERROR"),args); } }; void DOS_Shell::CMD_RMDIR(char * args) { StripSpaces(args); char * rem=ScanCMDRemain(args); if (rem) { WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem); return; } if (!DOS_RemoveDir(args)) { WriteOut(MSG_Get("SHELL_CMD_RMDIR_ERROR"),args); } }; static void FormatNumber(Bitu num,char * buf) { Bitu numm,numk,numb; numb=num % 1000; num/=1000; numk=num % 1000; num/=1000; numm=num; if (numm) { sprintf(buf,"%d,%03d,%03d",numm,numk,numb); return; }; if (numk) { sprintf(buf,"%d,%03d",numk,numb); return; }; sprintf(buf,"%d",numb); } void DOS_Shell::CMD_DIR(char * args) { char numformat[16]; char path[DOS_PATHLENGTH]; 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(line.c_str()); } bool optW=ScanCMDBool(args,"W"); bool optS=ScanCMDBool(args,"S"); bool optP=ScanCMDBool(args,"P"); bool optAD=ScanCMDBool(args,"AD"); char * rem=ScanCMDRemain(args); if (rem) { WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem); return; } Bit32u byte_count,file_count,dir_count; Bitu w_count=0; Bitu p_count=0; Bitu w_size = optW?5:1; byte_count=file_count=dir_count=0; char buffer[CROSS_LEN]; args = trim(args); Bit32u argLen = strlen(args); if (argLen == 0) { strcpy(args,"*.*"); //no arguments. } else { switch (args[argLen-1]) { case '\\': // handle \, C:\, etc. case ':' : // handle C:, etc. strcat(args,"*.*"); break; default: break; } } args = ExpandDot(args,buffer); if (!strrchr(args,'*') && !strrchr(args,'?')) { Bit16u attribute=0; if(DOS_GetFileAttr(args,&attribute) && (attribute&DOS_ATTR_DIRECTORY) ) { strcat(args,"\\*.*"); // if no wildcard and a directory, get its files } } if (!strrchr(args,'.')) { strcat(args,".*"); // if no extension, get them all } /* Make a full path in the args */ if (!DOS_Canonicalize(args,path)) { WriteOut(MSG_Get("SHELL_ILLEGAL_PATH")); return; } *(strrchr(path,'\\')+1)=0; WriteOut(MSG_Get("SHELL_CMD_DIR_INTRO"),path); DOS_DTA dta(dos.dta()); bool ret=DOS_FindFirst(args,0xffff & ~DOS_ATTR_VOLUME); if (!ret) { WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args); return; } do { /* File name and extension */ char name[DOS_NAMELENGTH_ASCII];Bit32u size;Bit16u date;Bit16u time;Bit8u attr; dta.GetResult(name,size,date,time,attr); /* Skip non-directories if option AD is present */ if(optAD && !(attr&DOS_ATTR_DIRECTORY) ) continue; char * ext=""; if (!optW && (name[0] != '.')) { ext = strrchr(name, '.'); if (!ext) ext = ""; else *ext++ = '\0'; } Bit8u day = date & 0x001f; Bit8u month = (date >> 5) & 0x000f; Bit16u year = (date >> 9) + 1980; Bit8u hour = (time >> 5 ) >> 6; Bit8u minute = (time >> 5) & 0x003f; /* output the file */ if (attr & DOS_ATTR_DIRECTORY) { if (optW) { WriteOut("[%s]",name); for (Bitu i=14-strlen(name);i>0;i--) WriteOut(" "); } else { WriteOut("%-8s %-3s %-16s %02d-%02d-%04d %2d:%02d\n",name,ext,"",day,month,year,hour,minute); } dir_count++; } else { if (optW) { WriteOut("%-16s",name); } else { FormatNumber(size,numformat); WriteOut("%-8s %-3s %16s %02d-%02d-%04d %2d:%02d\n",name,ext,numformat,day,month,year,hour,minute); } file_count++; byte_count+=size; } if (optW) { w_count++; } if(optP) { if(!(++p_count%(22*w_size))) { CMD_PAUSE(args); } } } while ( (ret=DOS_FindNext()) ); if (optW) { if (w_count%5) WriteOut("\n"); } /* Show the summary of results */ FormatNumber(byte_count,numformat); WriteOut(MSG_Get("SHELL_CMD_DIR_BYTES_USED"),file_count,numformat); Bit8u drive=dta.GetSearchDrive(); //TODO Free Space Bitu free_space=1024*1024*100; if (Drives[drive]) { Bit16u bytes_sector;Bit8u sectors_cluster;Bit16u total_clusters;Bit16u free_clusters; Drives[drive]->AllocationInfo(&bytes_sector,§ors_cluster,&total_clusters,&free_clusters); free_space=bytes_sector*sectors_cluster*free_clusters; } FormatNumber(free_space,numformat); WriteOut(MSG_Get("SHELL_CMD_DIR_BYTES_FREE"),dir_count,numformat); } void DOS_Shell::CMD_COPY(char * args) { static char defaulttarget[] = "."; StripSpaces(args); DOS_DTA dta(dos.dta()); Bit32u size;Bit16u date;Bit16u time;Bit8u attr; char name[DOS_NAMELENGTH_ASCII]; // ignore /b and /t switches: always copy binary ScanCMDBool(args,"B"); ScanCMDBool(args,"T"); char * rem=ScanCMDRemain(args); if (rem) { WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem); return; } // source/target char* source = StripWord(args); char* target = NULL; if (args && *args) target = StripWord(args); if (!target || !*target) target = defaulttarget; // Target and Source have to be there if (!source || !strlen(source)) { WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args); return; }; /* Make a full path in the args */ char pathSource[DOS_PATHLENGTH]; char pathTarget[DOS_PATHLENGTH]; if (!DOS_Canonicalize(source,pathSource)) { WriteOut(MSG_Get("SHELL_ILLEGAL_PATH")); return; } // cut search pattern char* pos = strrchr(pathSource,'\\'); if (pos) *(pos+1) = 0; if (!DOS_Canonicalize(target,pathTarget)) { WriteOut(MSG_Get("SHELL_ILLEGAL_PATH")); return; } char* temp = strstr(pathTarget,"*.*"); if(temp) *temp = 0;//strip off *.* from target // add '\\' if target is a directoy if (pathTarget[strlen(pathTarget)-1]!='\\') { if (DOS_FindFirst(pathTarget,0xffff & ~DOS_ATTR_VOLUME)) { dta.GetResult(name,size,date,time,attr); if (attr & DOS_ATTR_DIRECTORY) strcat(pathTarget,"\\"); } }; bool ret=DOS_FindFirst(source,0xffff & ~DOS_ATTR_VOLUME); if (!ret) { WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args); return; } Bit32u count = 0; Bit16u sourceHandle,targetHandle; char nameTarget[DOS_PATHLENGTH]; char nameSource[DOS_PATHLENGTH]; while (ret) { dta.GetResult(name,size,date,time,attr); if ((attr & DOS_ATTR_DIRECTORY)==0) { strcpy(nameSource,pathSource); strcat(nameSource,name); // Open Source if (DOS_OpenFile(nameSource,0,&sourceHandle)) { // Create Target strcpy(nameTarget,pathTarget); if (nameTarget[strlen(nameTarget)-1]=='\\') strcat(nameTarget,name); if (DOS_CreateFile(nameTarget,0,&targetHandle)) { // Copy Bit8u buffer[0x8000]; bool failed = false; Bit16u toread = 0x8000; do { failed |= DOS_ReadFile(sourceHandle,buffer,&toread); failed |= DOS_WriteFile(targetHandle,buffer,&toread); } while (toread==0x8000); failed |= DOS_CloseFile(sourceHandle); failed |= DOS_CloseFile(targetHandle); WriteOut(" %s\n",name); count++; } else { DOS_CloseFile(sourceHandle); WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),target); } } else WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),source); }; ret=DOS_FindNext(); }; WriteOut(MSG_Get("SHELL_CMD_COPY_SUCCESS"),count); } void DOS_Shell::CMD_SET(char * args) { StripSpaces(args); std::string line; if (!*args) { /* No command line show all environment lines */ Bitu count=GetEnvCount(); for (Bitu a=0;a=n) ==(!has_not)) DoCommand(args); return; } /* Normal if string compare */ if (!*args) { SyntaxError();return;}; char * word2=StripWord(args); if ((strcmp(word,word2)==0)==(!has_not)) DoCommand(args); } void DOS_Shell::CMD_GOTO(char * args) { StripSpaces(args); if (!bf) return; if (*args &&(*args==':')) args++; 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_TYPE(char * args) { StripSpaces(args); if (!*args) { WriteOut(MSG_Get("SHELL_SYNTAXERROR")); return; } Bit16u handle; char * word; nextfile: word=StripWord(args); if (!DOS_OpenFile(word,0,&handle)) { WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),word); return; } Bit16u n;Bit8u c; do { n=1; DOS_ReadFile(handle,&c,&n); DOS_WriteFile(STDOUT,&c,&n); } while (n); DOS_CloseFile(handle); if (*args) goto nextfile; } void DOS_Shell::CMD_REM(char * args) { } void DOS_Shell::CMD_PAUSE(char * args){ WriteOut(MSG_Get("SHELL_CMD_PAUSE")); Bit8u c;Bit16u n=1; DOS_ReadFile (STDIN,&c,&n); } void DOS_Shell::CMD_CALL(char * args){ this->call=true; /* else the old batchfile will be closed first */ this->ParseLine(args); this->call=false; } 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 */ localDrive* ldp=0; char mountstring[DOS_PATHLENGTH+CROSS_LEN+20]; char temp_str[2] = { 0,0 }; try { strcpy(mountstring,"MOUNT "); StripSpaces(args); std::string arg; CommandLine command(0,args); if (command.GetCount() != 2) throw 0 ; command.FindCommand(2,arg); if((arg=="/D" ) || (arg=="/d")) throw 1; //No removal (one day) command.FindCommand(1,arg); if(arg[1] !=':') throw(0); temp_str[0]=toupper(args[0]); if(Drives[temp_str[0]-'A'] ) throw 0; //targetdrive in use strcat(mountstring,temp_str); strcat(mountstring," "); command.FindCommand(2,arg); Bit8u drive;char fulldir[DOS_PATHLENGTH]; if (!DOS_MakeName(const_cast(arg.c_str()),fulldir,&drive)) throw 0; if( ( ldp=dynamic_cast(Drives[drive])) == 0 ) throw 0; 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){ if(a == 0) { WriteOut(MSG_Get("SHELL_CMD_SUBST_FAILURE")); } else { WriteOut(MSG_Get("SHELL_CMD_SUBST_NO_REMOVE")); } return; } catch(...) { //dynamic cast failed =>so no localdrive WriteOut(MSG_Get("SHELL_CMD_SUBST_FAILURE")); return; } return; } void DOS_Shell::CMD_LOADHIGH(char *args){ this->ParseLine(args); } void DOS_Shell::CMD_CHOICE(char * args){ static char defargs[] = "[YN]"; static char defchoice[] = "yn"; char *rem = NULL, *ptr; bool optN = false; if (args) { char *last = strchr(args,0); StripSpaces(args); optN=ScanCMDBool(args,"N"); rem=ScanCMDRemain(args); if (rem && *rem && (tolower(rem[1]) != 'c' || rem[2] != ':')) { WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem); return; } if (args == rem) args = strchr(rem,0)+1; if (rem) rem += 3; if (args > last) args = NULL; } if (!args || !*args) args = defargs; if (!rem || !*rem) rem = defchoice; ptr = rem; Bit8u c; while ((c = *ptr)) *ptr++ = tolower(c); WriteOut(args); if (!optN) WriteOut("\r\n"); Bit16u n=1; do { DOS_ReadFile (STDIN,&c,&n); } while (!c || !(ptr = strchr(rem,tolower(c)))); if (optN) { DOS_WriteFile (STDOUT,&c, &n); WriteOut("\r\n"); } dos.return_code = ptr-rem+1; } void DOS_Shell::CMD_ATTRIB(char *args){ // No-Op for now. }