diff --git a/src/misc/cross.cpp b/src/misc/cross.cpp index b64725d..31be629 100644 --- a/src/misc/cross.cpp +++ b/src/misc/cross.cpp @@ -1,265 +1,265 @@ -/* - * Copyright (C) 2002-2009 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: cross.cpp,v 1.7 2009/05/26 17:43:39 qbix79 Exp $ */ - -#include "dosbox.h" -#include "cross.h" -#include "support.h" -#include -#include - -#ifdef HW_RVL -#include "wiihardware.h" -#endif - -#ifdef WIN32 -#ifndef _WIN32_IE -#define _WIN32_IE 0x0400 -#endif -#include -#endif - -#if defined HAVE_SYS_TYPES_H && defined HAVE_PWD_H -#include -#include -#endif - -#ifdef WIN32 -static void W32_ConfDir(std::string& in,bool create) { - int c = create?1:0; - char result[MAX_PATH] = { 0 }; - BOOL r = SHGetSpecialFolderPath(NULL,result,CSIDL_LOCAL_APPDATA,c); - if(!r || result[0] == 0) r = SHGetSpecialFolderPath(NULL,result,CSIDL_APPDATA,c); - if(!r || result[0] == 0) { - char const * windir = getenv("windir"); - if(!windir) windir = "c:\\windows"; - safe_strncpy(result,windir,MAX_PATH); - char const* appdata = "\\Application Data"; - size_t len = strlen(result); - if(len + strlen(appdata) < MAX_PATH) strcat(result,appdata); - if(create) mkdir(result); - } - in = result; -} -#endif - -void Cross::GetPlatformConfigDir(std::string& in) { -#ifdef WIN32 - W32_ConfDir(in,false); - in += "\\DOSBox"; -#elif defined(MACOSX) - in = "~/Library/Preferences"; - ResolveHomedir(in); -#elif defined(HW_RVL) - in = "sd:/DOSBox"; -#else - in = "~/.dosbox"; - ResolveHomedir(in); -#endif - in += CROSS_FILESPLIT; -} - -void Cross::GetPlatformConfigName(std::string& in) { -#ifdef WIN32 -#define DEFAULT_CONFIG_FILE "dosbox-" VERSION ".conf" -#elif defined(MACOSX) -#define DEFAULT_CONFIG_FILE "DOSBox " VERSION " Preferences" -#elif defined(HW_RVL) -#define DEFAULT_CONFIG_FILE "dosbox.conf" -#else /*linux freebsd*/ -#define DEFAULT_CONFIG_FILE "dosbox-" VERSION ".conf" -#endif - in = DEFAULT_CONFIG_FILE; -} - -void Cross::CreatePlatformConfigDir(std::string& in) { -#ifdef WIN32 - W32_ConfDir(in,true); - in += "\\DOSBox"; - mkdir(in.c_str()); -#elif defined(MACOSX) - in = "~/Library/Preferences/"; - ResolveHomedir(in); - //Don't create it. Assume it exists -#elif defined(HW_RVL) - in = "sd:/DOSBox"; - CreateDir(in); -#else - in = "~/.dosbox"; - ResolveHomedir(in); - mkdir(in.c_str(),0700); -#endif - in += CROSS_FILESPLIT; -} - -void Cross::ResolveHomedir(std::string & temp_line) { - if(!temp_line.size() || temp_line[0] != '~') return; //No ~ - - if(temp_line.size() == 1 || temp_line[1] == CROSS_FILESPLIT) { //The ~ and ~/ variant - char * home = getenv("HOME"); - if(home) temp_line.replace(0,1,std::string(home)); -#if defined HAVE_SYS_TYPES_H && defined HAVE_PWD_H - } else { // The ~username variant - std::string::size_type namelen = temp_line.find(CROSS_FILESPLIT); - if(namelen == std::string::npos) namelen = temp_line.size(); - std::string username = temp_line.substr(1,namelen - 1); - struct passwd* pass = getpwnam(username.c_str()); - if(pass) temp_line.replace(0,namelen,pass->pw_dir); //namelen -1 +1(for the ~) -#endif // USERNAME lookup code - } -} - -void Cross::CreateDir(std::string const& in) { -#ifdef WIN32 - mkdir(in.c_str()); -#else - mkdir(in.c_str(),0700); -#endif -} - -#if defined (WIN32) - -dir_information* open_directory(const char* dirname) { - if (dirname == NULL) return NULL; - - size_t len = strlen(dirname); - if (len == 0) return NULL; - - static dir_information dir; - - safe_strncpy(dir.base_path,dirname,MAX_PATH); - - if (dirname[len-1] == '\\') strcat(dir.base_path,"*.*"); - else strcat(dir.base_path,"\\*.*"); - - dir.handle = INVALID_HANDLE_VALUE; - - return (access(dirname,0) ? NULL : &dir); -} - -bool read_directory_first(dir_information* dirp, char* entry_name, bool& is_directory) { - dirp->handle = FindFirstFile(dirp->base_path, &dirp->search_data); - if (INVALID_HANDLE_VALUE == dirp->handle) { - return false; - } - - safe_strncpy(entry_name,dirp->search_data.cFileName,(MAX_PATHsearch_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) is_directory = true; - else is_directory = false; - - return true; -} - -bool read_directory_next(dir_information* dirp, char* entry_name, bool& is_directory) { - int result = FindNextFile(dirp->handle, &dirp->search_data); - if (result==0) return false; - - safe_strncpy(entry_name,dirp->search_data.cFileName,(MAX_PATHsearch_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) is_directory = true; - else is_directory = false; - - return true; -} - -void close_directory(dir_information* dirp) { - if (dirp->handle != INVALID_HANDLE_VALUE) { - FindClose(dirp->handle); - dirp->handle = INVALID_HANDLE_VALUE; - } -} - -#else - -dir_information* open_directory(const char* dirname) { - static dir_information dir; - dir.dir=opendir(dirname); - safe_strncpy(dir.base_path,dirname,CROSS_LEN); - return dir.dir?&dir:NULL; -} - -bool read_directory_first(dir_information* dirp, char* entry_name, bool& is_directory) { - struct dirent* dentry = readdir(dirp->dir); - if (dentry==NULL) { - return false; - } - -// safe_strncpy(entry_name,dentry->d_name,(FILENAME_MAXd_name,CROSS_LEN); - -#ifdef DIRENT_HAS_D_TYPE - if(dentry->d_type == DT_DIR) { - is_directory = true; - return true; - } else if(dentry->d_type == DT_REG) { - is_directory = false; - return true; - } -#endif - - // probably use d_type here instead of a full stat() - static char buffer[2*CROSS_LEN] = { 0 }; - buffer[0] = 0; - strcpy(buffer,dirp->base_path); - strcat(buffer,entry_name); - struct stat status; - if (stat(buffer,&status)==0) is_directory = (S_ISDIR(status.st_mode)>0); - else is_directory = false; - - return true; -} - -bool read_directory_next(dir_information* dirp, char* entry_name, bool& is_directory) { - struct dirent* dentry = readdir(dirp->dir); - if (dentry==NULL) { - return false; - } - -// safe_strncpy(entry_name,dentry->d_name,(FILENAME_MAXd_name,CROSS_LEN); - -#ifdef DIRENT_HAS_D_TYPE - if(dentry->d_type == DT_DIR) { - is_directory = true; - return true; - } else if(dentry->d_type == DT_REG) { - is_directory = false; - return true; - } -#endif - - // probably use d_type here instead of a full stat() - static char buffer[2*CROSS_LEN] = { 0 }; - buffer[0] = 0; - strcpy(buffer,dirp->base_path); - strcat(buffer,entry_name); - struct stat status; - - if (stat(buffer,&status)==0) is_directory = (S_ISDIR(status.st_mode)>0); - else is_directory = false; - - return true; -} - -void close_directory(dir_information* dirp) { - closedir(dirp->dir); -} - -#endif +/* + * Copyright (C) 2002-2009 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: cross.cpp,v 1.7 2009-05-26 17:43:39 qbix79 Exp $ */ + +#include "dosbox.h" +#include "cross.h" +#include "support.h" +#include +#include + +#ifdef HW_RVL +#include "wiihardware.h" +#endif + +#ifdef WIN32 +#ifndef _WIN32_IE +#define _WIN32_IE 0x0400 +#endif +#include +#endif + +#if defined HAVE_SYS_TYPES_H && defined HAVE_PWD_H +#include +#include +#endif + +#ifdef WIN32 +static void W32_ConfDir(std::string& in,bool create) { + int c = create?1:0; + char result[MAX_PATH] = { 0 }; + BOOL r = SHGetSpecialFolderPath(NULL,result,CSIDL_LOCAL_APPDATA,c); + if(!r || result[0] == 0) r = SHGetSpecialFolderPath(NULL,result,CSIDL_APPDATA,c); + if(!r || result[0] == 0) { + char const * windir = getenv("windir"); + if(!windir) windir = "c:\\windows"; + safe_strncpy(result,windir,MAX_PATH); + char const* appdata = "\\Application Data"; + size_t len = strlen(result); + if(len + strlen(appdata) < MAX_PATH) strcat(result,appdata); + if(create) mkdir(result); + } + in = result; +} +#endif + +void Cross::GetPlatformConfigDir(std::string& in) { +#ifdef WIN32 + W32_ConfDir(in,false); + in += "\\DOSBox"; +#elif defined(MACOSX) + in = "~/Library/Preferences"; + ResolveHomedir(in); +#elif defined(HW_RVL) + in = "sd:/DOSBox"; +#else + in = "~/.dosbox"; + ResolveHomedir(in); +#endif + in += CROSS_FILESPLIT; +} + +void Cross::GetPlatformConfigName(std::string& in) { +#ifdef WIN32 +#define DEFAULT_CONFIG_FILE "dosbox-" VERSION ".conf" +#elif defined(MACOSX) +#define DEFAULT_CONFIG_FILE "DOSBox " VERSION " Preferences" +#elif defined(HW_RVL) +#define DEFAULT_CONFIG_FILE "dosbox.conf" +#else /*linux freebsd*/ +#define DEFAULT_CONFIG_FILE "dosbox-" VERSION ".conf" +#endif + in = DEFAULT_CONFIG_FILE; +} + +void Cross::CreatePlatformConfigDir(std::string& in) { +#ifdef WIN32 + W32_ConfDir(in,true); + in += "\\DOSBox"; + mkdir(in.c_str()); +#elif defined(MACOSX) + in = "~/Library/Preferences/"; + ResolveHomedir(in); + //Don't create it. Assume it exists +#elif defined(HW_RVL) + in = "sd:/DOSBox"; + CreateDir(in); +#else + in = "~/.dosbox"; + ResolveHomedir(in); + mkdir(in.c_str(),0700); +#endif + in += CROSS_FILESPLIT; +} + +void Cross::ResolveHomedir(std::string & temp_line) { + if(!temp_line.size() || temp_line[0] != '~') return; //No ~ + + if(temp_line.size() == 1 || temp_line[1] == CROSS_FILESPLIT) { //The ~ and ~/ variant + char * home = getenv("HOME"); + if(home) temp_line.replace(0,1,std::string(home)); +#if defined HAVE_SYS_TYPES_H && defined HAVE_PWD_H + } else { // The ~username variant + std::string::size_type namelen = temp_line.find(CROSS_FILESPLIT); + if(namelen == std::string::npos) namelen = temp_line.size(); + std::string username = temp_line.substr(1,namelen - 1); + struct passwd* pass = getpwnam(username.c_str()); + if(pass) temp_line.replace(0,namelen,pass->pw_dir); //namelen -1 +1(for the ~) +#endif // USERNAME lookup code + } +} + +void Cross::CreateDir(std::string const& in) { +#ifdef WIN32 + mkdir(in.c_str()); +#else + mkdir(in.c_str(),0700); +#endif +} + +#if defined (WIN32) + +dir_information* open_directory(const char* dirname) { + if (dirname == NULL) return NULL; + + size_t len = strlen(dirname); + if (len == 0) return NULL; + + static dir_information dir; + + safe_strncpy(dir.base_path,dirname,MAX_PATH); + + if (dirname[len-1] == '\\') strcat(dir.base_path,"*.*"); + else strcat(dir.base_path,"\\*.*"); + + dir.handle = INVALID_HANDLE_VALUE; + + return (access(dirname,0) ? NULL : &dir); +} + +bool read_directory_first(dir_information* dirp, char* entry_name, bool& is_directory) { + dirp->handle = FindFirstFile(dirp->base_path, &dirp->search_data); + if (INVALID_HANDLE_VALUE == dirp->handle) { + return false; + } + + safe_strncpy(entry_name,dirp->search_data.cFileName,(MAX_PATHsearch_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) is_directory = true; + else is_directory = false; + + return true; +} + +bool read_directory_next(dir_information* dirp, char* entry_name, bool& is_directory) { + int result = FindNextFile(dirp->handle, &dirp->search_data); + if (result==0) return false; + + safe_strncpy(entry_name,dirp->search_data.cFileName,(MAX_PATHsearch_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) is_directory = true; + else is_directory = false; + + return true; +} + +void close_directory(dir_information* dirp) { + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose(dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + } +} + +#else + +dir_information* open_directory(const char* dirname) { + static dir_information dir; + dir.dir=opendir(dirname); + safe_strncpy(dir.base_path,dirname,CROSS_LEN); + return dir.dir?&dir:NULL; +} + +bool read_directory_first(dir_information* dirp, char* entry_name, bool& is_directory) { + struct dirent* dentry = readdir(dirp->dir); + if (dentry==NULL) { + return false; + } + +// safe_strncpy(entry_name,dentry->d_name,(FILENAME_MAXd_name,CROSS_LEN); + +#ifdef DIRENT_HAS_D_TYPE + if(dentry->d_type == DT_DIR) { + is_directory = true; + return true; + } else if(dentry->d_type == DT_REG) { + is_directory = false; + return true; + } +#endif + + // probably use d_type here instead of a full stat() + static char buffer[2*CROSS_LEN] = { 0 }; + buffer[0] = 0; + strcpy(buffer,dirp->base_path); + strcat(buffer,entry_name); + struct stat status; + if (stat(buffer,&status)==0) is_directory = (S_ISDIR(status.st_mode)>0); + else is_directory = false; + + return true; +} + +bool read_directory_next(dir_information* dirp, char* entry_name, bool& is_directory) { + struct dirent* dentry = readdir(dirp->dir); + if (dentry==NULL) { + return false; + } + +// safe_strncpy(entry_name,dentry->d_name,(FILENAME_MAXd_name,CROSS_LEN); + +#ifdef DIRENT_HAS_D_TYPE + if(dentry->d_type == DT_DIR) { + is_directory = true; + return true; + } else if(dentry->d_type == DT_REG) { + is_directory = false; + return true; + } +#endif + + // probably use d_type here instead of a full stat() + static char buffer[2*CROSS_LEN] = { 0 }; + buffer[0] = 0; + strcpy(buffer,dirp->base_path); + strcat(buffer,entry_name); + struct stat status; + + if (stat(buffer,&status)==0) is_directory = (S_ISDIR(status.st_mode)>0); + else is_directory = false; + + return true; +} + +void close_directory(dir_information* dirp) { + closedir(dirp->dir); +} + +#endif diff --git a/src/misc/messages.cpp b/src/misc/messages.cpp index 139e8c0..b2ce3d9 100644 --- a/src/misc/messages.cpp +++ b/src/misc/messages.cpp @@ -1,144 +1,144 @@ -/* - * Copyright (C) 2002-2009 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: messages.cpp,v 1.23 2009/06/17 08:52:35 qbix79 Exp $ */ - -#include -#include -#include -#include "dosbox.h" -#include "cross.h" -#include "support.h" -#include "setup.h" -#include "control.h" -#include -#include -using namespace std; - - - -#define LINE_IN_MAXLEN 2048 - -struct MessageBlock { - string name; - string val; - MessageBlock(const char* _name, const char* _val): - name(_name),val(_val){} -}; - -static list Lang; -typedef list::iterator itmb; - -void MSG_Add(const char * _name, const char* _val) { - /* Find the message */ - for(itmb tel=Lang.begin();tel!=Lang.end();tel++) { - if((*tel).name==_name) { -// LOG_MSG("double entry for %s",_name); //Message file might be loaded before default text messages - return; - } - } - /* if the message doesn't exist add it */ - Lang.push_back(MessageBlock(_name,_val)); -} - -void MSG_Replace(const char * _name, const char* _val) { - /* Find the message */ - for(itmb tel=Lang.begin();tel!=Lang.end();tel++) { - if((*tel).name==_name) { - Lang.erase(tel); - break; - } - } - /* Even if the message doesn't exist add it */ - Lang.push_back(MessageBlock(_name,_val)); -} - -static void LoadMessageFile(const char * fname) { - if (!fname) return; - if(*fname=='\0') return;//empty string=no languagefile - FILE * mfile=fopen(fname,"rt"); - /* This should never happen and since other modules depend on this use a normal printf */ - if (!mfile) { - E_Exit("MSG:Can't load messages: %s",fname); - } - char linein[LINE_IN_MAXLEN]; - char name[LINE_IN_MAXLEN]; - char string[LINE_IN_MAXLEN*10]; - /* Start out with empty strings */ - name[0]=0;string[0]=0; - while(fgets(linein, LINE_IN_MAXLEN, mfile)!=0) { - /* Parse the read line */ - /* First remove characters 10 and 13 from the line */ - char * parser=linein; - char * writer=linein; - while (*parser) { - if (*parser!=10 && *parser!=13) { - *writer++=*parser; - } - *parser++; - } - *writer=0; - /* New string name */ - if (linein[0]==':') { - string[0]=0; - strcpy(name,linein+1); - /* End of string marker */ - } else if (linein[0]=='.') { - /* Replace/Add the string to the internal langaugefile */ - /* Remove last newline (marker is \n.\n) */ - size_t ll = strlen(string); - if(ll && string[ll - 1] == '\n') string[ll - 1] = 0; //Second if should not be needed, but better be safe. - MSG_Replace(name,string); - } else { - /* Normal string to be added */ - strcat(string,linein); - strcat(string,"\n"); - } - } - fclose(mfile); -} - -const char * MSG_Get(char const * msg) { - for(itmb tel=Lang.begin();tel!=Lang.end();tel++){ - if((*tel).name==msg) - { - return (*tel).val.c_str(); - } - } - return "Message not Found!\n"; -} - - -void MSG_Write(const char * location) { - FILE* out=fopen(location,"w+t"); - if(out==NULL) return;//maybe an error? - for(itmb tel=Lang.begin();tel!=Lang.end();tel++){ - fprintf(out,":%s\n%s\n.\n",(*tel).name.c_str(),(*tel).val.c_str()); - } - fclose(out); -} - -void MSG_Init(Section_prop * section) { - std::string file_name; - if (control->cmdline->FindString("-lang",file_name,true)) { - LoadMessageFile(file_name.c_str()); - } else { - Prop_path* pathprop = section->Get_path("language"); - if(pathprop) LoadMessageFile(pathprop->realpath.c_str()); - } -} +/* + * Copyright (C) 2002-2009 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: messages.cpp,v 1.23 2009-06-17 08:52:35 qbix79 Exp $ */ + +#include +#include +#include +#include "dosbox.h" +#include "cross.h" +#include "support.h" +#include "setup.h" +#include "control.h" +#include +#include +using namespace std; + + + +#define LINE_IN_MAXLEN 2048 + +struct MessageBlock { + string name; + string val; + MessageBlock(const char* _name, const char* _val): + name(_name),val(_val){} +}; + +static list Lang; +typedef list::iterator itmb; + +void MSG_Add(const char * _name, const char* _val) { + /* Find the message */ + for(itmb tel=Lang.begin();tel!=Lang.end();tel++) { + if((*tel).name==_name) { +// LOG_MSG("double entry for %s",_name); //Message file might be loaded before default text messages + return; + } + } + /* if the message doesn't exist add it */ + Lang.push_back(MessageBlock(_name,_val)); +} + +void MSG_Replace(const char * _name, const char* _val) { + /* Find the message */ + for(itmb tel=Lang.begin();tel!=Lang.end();tel++) { + if((*tel).name==_name) { + Lang.erase(tel); + break; + } + } + /* Even if the message doesn't exist add it */ + Lang.push_back(MessageBlock(_name,_val)); +} + +static void LoadMessageFile(const char * fname) { + if (!fname) return; + if(*fname=='\0') return;//empty string=no languagefile + FILE * mfile=fopen(fname,"rt"); + /* This should never happen and since other modules depend on this use a normal printf */ + if (!mfile) { + E_Exit("MSG:Can't load messages: %s",fname); + } + char linein[LINE_IN_MAXLEN]; + char name[LINE_IN_MAXLEN]; + char string[LINE_IN_MAXLEN*10]; + /* Start out with empty strings */ + name[0]=0;string[0]=0; + while(fgets(linein, LINE_IN_MAXLEN, mfile)!=0) { + /* Parse the read line */ + /* First remove characters 10 and 13 from the line */ + char * parser=linein; + char * writer=linein; + while (*parser) { + if (*parser!=10 && *parser!=13) { + *writer++=*parser; + } + *parser++; + } + *writer=0; + /* New string name */ + if (linein[0]==':') { + string[0]=0; + strcpy(name,linein+1); + /* End of string marker */ + } else if (linein[0]=='.') { + /* Replace/Add the string to the internal langaugefile */ + /* Remove last newline (marker is \n.\n) */ + size_t ll = strlen(string); + if(ll && string[ll - 1] == '\n') string[ll - 1] = 0; //Second if should not be needed, but better be safe. + MSG_Replace(name,string); + } else { + /* Normal string to be added */ + strcat(string,linein); + strcat(string,"\n"); + } + } + fclose(mfile); +} + +const char * MSG_Get(char const * msg) { + for(itmb tel=Lang.begin();tel!=Lang.end();tel++){ + if((*tel).name==msg) + { + return (*tel).val.c_str(); + } + } + return "Message not Found!\n"; +} + + +void MSG_Write(const char * location) { + FILE* out=fopen(location,"w+t"); + if(out==NULL) return;//maybe an error? + for(itmb tel=Lang.begin();tel!=Lang.end();tel++){ + fprintf(out,":%s\n%s\n.\n",(*tel).name.c_str(),(*tel).val.c_str()); + } + fclose(out); +} + +void MSG_Init(Section_prop * section) { + std::string file_name; + if (control->cmdline->FindString("-lang",file_name,true)) { + LoadMessageFile(file_name.c_str()); + } else { + Prop_path* pathprop = section->Get_path("language"); + if(pathprop) LoadMessageFile(pathprop->realpath.c_str()); + } +} diff --git a/src/misc/programs.cpp b/src/misc/programs.cpp index d8bfa3c..b67aee7 100644 --- a/src/misc/programs.cpp +++ b/src/misc/programs.cpp @@ -1,392 +1,392 @@ -/* - * Copyright (C) 2002-2009 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: programs.cpp,v 1.37 2009/05/27 09:15:42 qbix79 Exp $ */ - -#include -#include -#include -#include -#include -#include -#include "programs.h" -#include "callback.h" -#include "regs.h" -#include "support.h" -#include "cross.h" -#include "control.h" -#include "shell.h" - -Bitu call_program; - -/* This registers a file on the virtual drive and creates the correct structure for it*/ - -static Bit8u exe_block[]={ - 0xbc,0x00,0x04, //MOV SP,0x400 decrease stack size - 0xbb,0x40,0x00, //MOV BX,0x040 for memory resize - 0xb4,0x4a, //MOV AH,0x4A Resize memory block - 0xcd,0x21, //INT 0x21 -//pos 12 is callback number - 0xFE,0x38,0x00,0x00, //CALLBack number - 0xb8,0x00,0x4c, //Mov ax,4c00 - 0xcd,0x21, //INT 0x21 -}; - -#define CB_POS 12 - -static std::vector internal_progs; - -void PROGRAMS_MakeFile(char const * const name,PROGRAMS_Main * main) { - Bit8u * comdata=(Bit8u *)malloc(32); //MEM LEAK - memcpy(comdata,&exe_block,sizeof(exe_block)); - comdata[CB_POS]=(Bit8u)(call_program&0xff); - comdata[CB_POS+1]=(Bit8u)((call_program>>8)&0xff); - - /* Copy save the pointer in the vector and save it's index */ - if (internal_progs.size()>255) E_Exit("PROGRAMS_MakeFile program size too large (%d)",static_cast(internal_progs.size())); - Bit8u index = (Bit8u)internal_progs.size(); - internal_progs.push_back(main); - - memcpy(&comdata[sizeof(exe_block)],&index,sizeof(index)); - Bit32u size=sizeof(exe_block)+sizeof(index); - VFILE_Register(name,comdata,size); -} - - - -static Bitu PROGRAMS_Handler(void) { - /* This sets up everything for a program start up call */ - Bitu size=sizeof(Bit8u); - Bit8u index; - /* Read the index from program code in memory */ - PhysPt reader=PhysMake(dos.psp(),256+sizeof(exe_block)); - HostPt writer=(HostPt)&index; - for (;size>0;size--) *writer++=mem_readb(reader++); - Program * new_program; - if(index > internal_progs.size()) E_Exit("something is messing with the memory"); - PROGRAMS_Main * handler = internal_progs[index]; - (*handler)(&new_program); - new_program->Run(); - delete new_program; - return CBRET_NONE; -} - - -/* Main functions used in all program */ - - -Program::Program() { - /* Find the command line and setup the PSP */ - psp = new DOS_PSP(dos.psp()); - /* Scan environment for filename */ - PhysPt envscan=PhysMake(psp->GetEnvironment(),0); - while (mem_readb(envscan)) envscan+=mem_strlen(envscan)+1; - envscan+=3; - CommandTail tail; - MEM_BlockRead(PhysMake(dos.psp(),128),&tail,128); - if (tail.count<127) tail.buffer[tail.count]=0; - else tail.buffer[126]=0; - char filename[256+1]; - MEM_StrCopy(envscan,filename,256); - cmd = new CommandLine(filename,tail.buffer); -} - -extern std::string full_arguments; - -void Program::ChangeToLongCmd() { - /* - * Get arguments directly from the shell instead of the psp. - * this is done in securemode: (as then the arguments to mount and friends - * can only be given on the shell ( so no int 21 4b) - * Securemode part is disabled as each of the internal command has already - * protection for it. (and it breaks games like cdman) - * it is also done for long arguments to as it is convient (as the total commandline can be longer then 127 characters. - * imgmount with lot's of parameters - * Length of arguments can be ~120. but switch when above 100 to be sure - */ - - if(/*control->SecureMode() ||*/ cmd->Get_arglength() > 100) { - CommandLine* temp = new CommandLine(cmd->GetFileName(),full_arguments.c_str()); - delete cmd; - cmd = temp; - } - full_arguments.assign(""); //Clear so it gets even more save -} - -void Program::WriteOut(const char * format,...) { - char buf[2048]; - va_list msg; - - va_start(msg,format); - vsnprintf(buf,2047,format,msg); - va_end(msg); - - Bit16u size = (Bit16u)strlen(buf); - DOS_WriteFile(STDOUT,(Bit8u *)buf,&size); -} - -void Program::WriteOut_NoParsing(const char * format) { - Bit16u size = (Bit16u)strlen(format); - DOS_WriteFile(STDOUT,(Bit8u *)format,&size); -} - - -bool Program::GetEnvStr(const char * entry,std::string & result) { - /* Walk through the internal environment and see for a match */ - PhysPt env_read=PhysMake(psp->GetEnvironment(),0); - char env_string[1024+1]; - result.erase(); - if (!entry[0]) return false; - do { - MEM_StrCopy(env_read,env_string,1024); - if (!env_string[0]) return false; - env_read += (PhysPt)(strlen(env_string)+1); - char* equal = strchr(env_string,'='); - if (!equal) continue; - /* replace the = with \0 to get the length */ - *equal = 0; - if (strlen(env_string) != strlen(entry)) continue; - if (strcasecmp(entry,env_string)!=0) continue; - /* restore the = to get the original result */ - *equal = '='; - result = env_string; - return true; - } while (1); - return false; -} - -bool Program::GetEnvNum(Bitu num,std::string & result) { - char env_string[1024+1]; - PhysPt env_read=PhysMake(psp->GetEnvironment(),0); - do { - MEM_StrCopy(env_read,env_string,1024); - if (!env_string[0]) break; - if (!num) { result=env_string;return true;} - env_read += (PhysPt)(strlen(env_string)+1); - num--; - } while (1); - return false; -} - -Bitu Program::GetEnvCount(void) { - PhysPt env_read=PhysMake(psp->GetEnvironment(),0); - Bitu num=0; - while (mem_readb(env_read)!=0) { - for (;mem_readb(env_read);env_read++) {}; - env_read++; - num++; - } - return num; -} - -bool Program::SetEnv(const char * entry,const char * new_string) { - PhysPt env_read=PhysMake(psp->GetEnvironment(),0); - PhysPt env_write=env_read; - char env_string[1024+1]; - do { - MEM_StrCopy(env_read,env_string,1024); - if (!env_string[0]) break; - env_read += (PhysPt)(strlen(env_string)+1); - if (!strchr(env_string,'=')) continue; /* Remove corrupt entry? */ - if ((strncasecmp(entry,env_string,strlen(entry))==0) && - env_string[strlen(entry)]=='=') continue; - MEM_BlockWrite(env_write,env_string,(Bitu)(strlen(env_string)+1)); - env_write += (PhysPt)(strlen(env_string)+1); - } while (1); -/* TODO Maybe save the program name sometime. not really needed though */ - /* Save the new entry */ - if (new_string[0]) { - std::string bigentry(entry); - for (std::string::iterator it = bigentry.begin(); it != bigentry.end(); ++it) *it = toupper(*it); - sprintf(env_string,"%s=%s",bigentry.c_str(),new_string); -// sprintf(env_string,"%s=%s",entry,new_string); //oldcode - MEM_BlockWrite(env_write,env_string,(Bitu)(strlen(env_string)+1)); - env_write += (PhysPt)(strlen(env_string)+1); - } - /* Clear out the final piece of the environment */ - mem_writed(env_write,0); - return true; -} - -class CONFIG : public Program { -public: - void Run(void); -}; - -void MSG_Write(const char *); - -void CONFIG::Run(void) { - FILE * f; - if (cmd->FindString("-writeconf",temp_line,true) - || cmd->FindString("-wc",temp_line,true)) { - /* In secure mode don't allow a new configfile to be created */ - if(control->SecureMode()) { - WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW")); - return; - } - f=fopen(temp_line.c_str(),"wb+"); - if (!f) { - WriteOut(MSG_Get("PROGRAM_CONFIG_FILE_ERROR"),temp_line.c_str()); - return; - } - fclose(f); - control->PrintConfig(temp_line.c_str()); - return; - } - if (cmd->FindString("-writelang",temp_line,true) - ||cmd->FindString("-wl",temp_line,true)) { - /* In secure mode don't allow a new languagefile to be created - * Who knows which kind of file we would overwriting. */ - if(control->SecureMode()) { - WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW")); - return; - } - f=fopen(temp_line.c_str(),"wb+"); - if (!f) { - WriteOut(MSG_Get("PROGRAM_CONFIG_FILE_ERROR"),temp_line.c_str()); - return; - } - fclose(f); - MSG_Write(temp_line.c_str()); - return; - } - - /* Code for switching to secure mode */ - if(cmd->FindExist("-securemode",true)) { - control->SwitchToSecureMode(); - WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_ON")); - return; - } - - /* Code for getting the current configuration. * - * Official format: config -get "section property" * - * As a bonus it will set %CONFIG% to this value as well */ - if(cmd->FindString("-get",temp_line,true)) { - std::string temp2 = ""; - cmd->GetStringRemain(temp2);//So -get n1 n2= can be used without quotes - if(temp2 != "") temp_line = temp_line + " " + temp2; - - std::string::size_type space = temp_line.find(" "); - if(space == std::string::npos) { - WriteOut(MSG_Get("PROGRAM_CONFIG_GET_SYNTAX")); - return; - } - //Copy the found property to a new string and erase from templine (mind the space) - std::string prop = temp_line.substr(space+1); temp_line.erase(space); - - Section* sec = control->GetSection(temp_line.c_str()); - if(!sec) { - WriteOut(MSG_Get("PROGRAM_CONFIG_SECTION_ERROR"),temp_line.c_str()); - return; - } - std::string val = sec->GetPropValue(prop.c_str()); - if(val == NO_SUCH_PROPERTY) { - WriteOut(MSG_Get("PROGRAM_CONFIG_NO_PROPERTY"),prop.c_str(),temp_line.c_str()); - return; - } - WriteOut("%s",val.c_str()); - first_shell->SetEnv("CONFIG",val.c_str()); - return; - } - - - - /* Code for the configuration changes * - * Official format: config -set "section property=value" * - * Accepted: without quotes and/or without -set and/or without section * - * and/or the "=" replaced by a " " */ - - if (cmd->FindString("-set",temp_line,true)) { //get all arguments - std::string temp2 = ""; - cmd->GetStringRemain(temp2);//So -set n1 n2=n3 can be used without quotes - if(temp2!="") temp_line = temp_line + " " + temp2; - } else if(!cmd->GetStringRemain(temp_line)) {//no set - WriteOut(MSG_Get("PROGRAM_CONFIG_USAGE")); //and no arguments specified - return; - }; - //Wanted input: n1 n2=n3 - char copy[1024]; - strcpy(copy,temp_line.c_str()); - //seperate section from property - const char* temp = strchr(copy,' '); - if((temp && *temp) || (temp=strchr(copy,'=')) ) copy[temp++ - copy]= 0; - else { - WriteOut(MSG_Get("PROGRAM_CONFIG_USAGE")); - return; - } - //if n1 n2 n3 then replace last space with = - const char* sign = strchr(temp,'='); - if(!sign) { - sign = strchr(temp,' '); - if(sign) { - copy[sign - copy] = '='; - } else { - //2 items specified (no space nor = between n2 and n3 - //assume that they posted: property value - //Try to determine the section. - Section* sec=control->GetSectionFromProperty(copy); - if(!sec){ - if(control->GetSectionFromProperty(temp)) return; //Weird situation:ignore - WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR"),copy); - return; - } //Hack to allow config ems true - char buffer[1024];strcpy(buffer,copy);strcat(buffer,"=");strcat(buffer,temp); - sign = strchr(buffer,' '); - if(sign) buffer[sign - buffer] = '='; - strcpy(copy,sec->GetName()); - temp = buffer; - } - } - - /* Input processed. Now the real job starts - * copy contains the likely "sectionname" - * temp contains "property=value" - * the section is destroyed and a new input line is given to - * the configuration parser. Then the section is restarted. - */ - char* inputline = const_cast(temp); - Section* sec = 0; - sec = control->GetSection(copy); - if(!sec) { WriteOut(MSG_Get("PROGRAM_CONFIG_SECTION_ERROR"),copy);return;} - sec->ExecuteDestroy(false); - sec->HandleInputline(inputline); - sec->ExecuteInit(false); - return; -} - - -static void CONFIG_ProgramStart(Program * * make) { - *make=new CONFIG; -} - - -void PROGRAMS_Init(Section* /*sec*/) { - /* Setup a special callback to start virtual programs */ - call_program=CALLBACK_Allocate(); - CALLBACK_Setup(call_program,&PROGRAMS_Handler,CB_RETF,"internal program"); - PROGRAMS_MakeFile("CONFIG.COM",CONFIG_ProgramStart); - - MSG_Add("PROGRAM_CONFIG_FILE_ERROR","Can't open file %s\n"); - MSG_Add("PROGRAM_CONFIG_USAGE","Config tool:\nUse -writeconf filename to write the current config.\nUse -writelang filename to write the current language strings.\n"); - MSG_Add("PROGRAM_CONFIG_SECURE_ON","Switched to secure mode.\n"); - MSG_Add("PROGRAM_CONFIG_SECURE_DISALLOW","This operation is not permitted in secure mode.\n"); - MSG_Add("PROGRAM_CONFIG_SECTION_ERROR","Section %s doesn't exist.\n"); - MSG_Add("PROGRAM_CONFIG_PROPERTY_ERROR","No such section or property.\n"); - MSG_Add("PROGRAM_CONFIG_NO_PROPERTY","There is no property %s in section %s.\n"); - MSG_Add("PROGRAM_CONFIG_GET_SYNTAX","Correct syntax: config -get \"section property\".\n"); -} +/* + * Copyright (C) 2002-2009 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: programs.cpp,v 1.37 2009-05-27 09:15:42 qbix79 Exp $ */ + +#include +#include +#include +#include +#include +#include +#include "programs.h" +#include "callback.h" +#include "regs.h" +#include "support.h" +#include "cross.h" +#include "control.h" +#include "shell.h" + +Bitu call_program; + +/* This registers a file on the virtual drive and creates the correct structure for it*/ + +static Bit8u exe_block[]={ + 0xbc,0x00,0x04, //MOV SP,0x400 decrease stack size + 0xbb,0x40,0x00, //MOV BX,0x040 for memory resize + 0xb4,0x4a, //MOV AH,0x4A Resize memory block + 0xcd,0x21, //INT 0x21 +//pos 12 is callback number + 0xFE,0x38,0x00,0x00, //CALLBack number + 0xb8,0x00,0x4c, //Mov ax,4c00 + 0xcd,0x21, //INT 0x21 +}; + +#define CB_POS 12 + +static std::vector internal_progs; + +void PROGRAMS_MakeFile(char const * const name,PROGRAMS_Main * main) { + Bit8u * comdata=(Bit8u *)malloc(32); //MEM LEAK + memcpy(comdata,&exe_block,sizeof(exe_block)); + comdata[CB_POS]=(Bit8u)(call_program&0xff); + comdata[CB_POS+1]=(Bit8u)((call_program>>8)&0xff); + + /* Copy save the pointer in the vector and save it's index */ + if (internal_progs.size()>255) E_Exit("PROGRAMS_MakeFile program size too large (%d)",static_cast(internal_progs.size())); + Bit8u index = (Bit8u)internal_progs.size(); + internal_progs.push_back(main); + + memcpy(&comdata[sizeof(exe_block)],&index,sizeof(index)); + Bit32u size=sizeof(exe_block)+sizeof(index); + VFILE_Register(name,comdata,size); +} + + + +static Bitu PROGRAMS_Handler(void) { + /* This sets up everything for a program start up call */ + Bitu size=sizeof(Bit8u); + Bit8u index; + /* Read the index from program code in memory */ + PhysPt reader=PhysMake(dos.psp(),256+sizeof(exe_block)); + HostPt writer=(HostPt)&index; + for (;size>0;size--) *writer++=mem_readb(reader++); + Program * new_program; + if(index > internal_progs.size()) E_Exit("something is messing with the memory"); + PROGRAMS_Main * handler = internal_progs[index]; + (*handler)(&new_program); + new_program->Run(); + delete new_program; + return CBRET_NONE; +} + + +/* Main functions used in all program */ + + +Program::Program() { + /* Find the command line and setup the PSP */ + psp = new DOS_PSP(dos.psp()); + /* Scan environment for filename */ + PhysPt envscan=PhysMake(psp->GetEnvironment(),0); + while (mem_readb(envscan)) envscan+=mem_strlen(envscan)+1; + envscan+=3; + CommandTail tail; + MEM_BlockRead(PhysMake(dos.psp(),128),&tail,128); + if (tail.count<127) tail.buffer[tail.count]=0; + else tail.buffer[126]=0; + char filename[256+1]; + MEM_StrCopy(envscan,filename,256); + cmd = new CommandLine(filename,tail.buffer); +} + +extern std::string full_arguments; + +void Program::ChangeToLongCmd() { + /* + * Get arguments directly from the shell instead of the psp. + * this is done in securemode: (as then the arguments to mount and friends + * can only be given on the shell ( so no int 21 4b) + * Securemode part is disabled as each of the internal command has already + * protection for it. (and it breaks games like cdman) + * it is also done for long arguments to as it is convient (as the total commandline can be longer then 127 characters. + * imgmount with lot's of parameters + * Length of arguments can be ~120. but switch when above 100 to be sure + */ + + if(/*control->SecureMode() ||*/ cmd->Get_arglength() > 100) { + CommandLine* temp = new CommandLine(cmd->GetFileName(),full_arguments.c_str()); + delete cmd; + cmd = temp; + } + full_arguments.assign(""); //Clear so it gets even more save +} + +void Program::WriteOut(const char * format,...) { + char buf[2048]; + va_list msg; + + va_start(msg,format); + vsnprintf(buf,2047,format,msg); + va_end(msg); + + Bit16u size = (Bit16u)strlen(buf); + DOS_WriteFile(STDOUT,(Bit8u *)buf,&size); +} + +void Program::WriteOut_NoParsing(const char * format) { + Bit16u size = (Bit16u)strlen(format); + DOS_WriteFile(STDOUT,(Bit8u *)format,&size); +} + + +bool Program::GetEnvStr(const char * entry,std::string & result) { + /* Walk through the internal environment and see for a match */ + PhysPt env_read=PhysMake(psp->GetEnvironment(),0); + char env_string[1024+1]; + result.erase(); + if (!entry[0]) return false; + do { + MEM_StrCopy(env_read,env_string,1024); + if (!env_string[0]) return false; + env_read += (PhysPt)(strlen(env_string)+1); + char* equal = strchr(env_string,'='); + if (!equal) continue; + /* replace the = with \0 to get the length */ + *equal = 0; + if (strlen(env_string) != strlen(entry)) continue; + if (strcasecmp(entry,env_string)!=0) continue; + /* restore the = to get the original result */ + *equal = '='; + result = env_string; + return true; + } while (1); + return false; +} + +bool Program::GetEnvNum(Bitu num,std::string & result) { + char env_string[1024+1]; + PhysPt env_read=PhysMake(psp->GetEnvironment(),0); + do { + MEM_StrCopy(env_read,env_string,1024); + if (!env_string[0]) break; + if (!num) { result=env_string;return true;} + env_read += (PhysPt)(strlen(env_string)+1); + num--; + } while (1); + return false; +} + +Bitu Program::GetEnvCount(void) { + PhysPt env_read=PhysMake(psp->GetEnvironment(),0); + Bitu num=0; + while (mem_readb(env_read)!=0) { + for (;mem_readb(env_read);env_read++) {}; + env_read++; + num++; + } + return num; +} + +bool Program::SetEnv(const char * entry,const char * new_string) { + PhysPt env_read=PhysMake(psp->GetEnvironment(),0); + PhysPt env_write=env_read; + char env_string[1024+1]; + do { + MEM_StrCopy(env_read,env_string,1024); + if (!env_string[0]) break; + env_read += (PhysPt)(strlen(env_string)+1); + if (!strchr(env_string,'=')) continue; /* Remove corrupt entry? */ + if ((strncasecmp(entry,env_string,strlen(entry))==0) && + env_string[strlen(entry)]=='=') continue; + MEM_BlockWrite(env_write,env_string,(Bitu)(strlen(env_string)+1)); + env_write += (PhysPt)(strlen(env_string)+1); + } while (1); +/* TODO Maybe save the program name sometime. not really needed though */ + /* Save the new entry */ + if (new_string[0]) { + std::string bigentry(entry); + for (std::string::iterator it = bigentry.begin(); it != bigentry.end(); ++it) *it = toupper(*it); + sprintf(env_string,"%s=%s",bigentry.c_str(),new_string); +// sprintf(env_string,"%s=%s",entry,new_string); //oldcode + MEM_BlockWrite(env_write,env_string,(Bitu)(strlen(env_string)+1)); + env_write += (PhysPt)(strlen(env_string)+1); + } + /* Clear out the final piece of the environment */ + mem_writed(env_write,0); + return true; +} + +class CONFIG : public Program { +public: + void Run(void); +}; + +void MSG_Write(const char *); + +void CONFIG::Run(void) { + FILE * f; + if (cmd->FindString("-writeconf",temp_line,true) + || cmd->FindString("-wc",temp_line,true)) { + /* In secure mode don't allow a new configfile to be created */ + if(control->SecureMode()) { + WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW")); + return; + } + f=fopen(temp_line.c_str(),"wb+"); + if (!f) { + WriteOut(MSG_Get("PROGRAM_CONFIG_FILE_ERROR"),temp_line.c_str()); + return; + } + fclose(f); + control->PrintConfig(temp_line.c_str()); + return; + } + if (cmd->FindString("-writelang",temp_line,true) + ||cmd->FindString("-wl",temp_line,true)) { + /* In secure mode don't allow a new languagefile to be created + * Who knows which kind of file we would overwriting. */ + if(control->SecureMode()) { + WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW")); + return; + } + f=fopen(temp_line.c_str(),"wb+"); + if (!f) { + WriteOut(MSG_Get("PROGRAM_CONFIG_FILE_ERROR"),temp_line.c_str()); + return; + } + fclose(f); + MSG_Write(temp_line.c_str()); + return; + } + + /* Code for switching to secure mode */ + if(cmd->FindExist("-securemode",true)) { + control->SwitchToSecureMode(); + WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_ON")); + return; + } + + /* Code for getting the current configuration. * + * Official format: config -get "section property" * + * As a bonus it will set %CONFIG% to this value as well */ + if(cmd->FindString("-get",temp_line,true)) { + std::string temp2 = ""; + cmd->GetStringRemain(temp2);//So -get n1 n2= can be used without quotes + if(temp2 != "") temp_line = temp_line + " " + temp2; + + std::string::size_type space = temp_line.find(" "); + if(space == std::string::npos) { + WriteOut(MSG_Get("PROGRAM_CONFIG_GET_SYNTAX")); + return; + } + //Copy the found property to a new string and erase from templine (mind the space) + std::string prop = temp_line.substr(space+1); temp_line.erase(space); + + Section* sec = control->GetSection(temp_line.c_str()); + if(!sec) { + WriteOut(MSG_Get("PROGRAM_CONFIG_SECTION_ERROR"),temp_line.c_str()); + return; + } + std::string val = sec->GetPropValue(prop.c_str()); + if(val == NO_SUCH_PROPERTY) { + WriteOut(MSG_Get("PROGRAM_CONFIG_NO_PROPERTY"),prop.c_str(),temp_line.c_str()); + return; + } + WriteOut("%s",val.c_str()); + first_shell->SetEnv("CONFIG",val.c_str()); + return; + } + + + + /* Code for the configuration changes * + * Official format: config -set "section property=value" * + * Accepted: without quotes and/or without -set and/or without section * + * and/or the "=" replaced by a " " */ + + if (cmd->FindString("-set",temp_line,true)) { //get all arguments + std::string temp2 = ""; + cmd->GetStringRemain(temp2);//So -set n1 n2=n3 can be used without quotes + if(temp2!="") temp_line = temp_line + " " + temp2; + } else if(!cmd->GetStringRemain(temp_line)) {//no set + WriteOut(MSG_Get("PROGRAM_CONFIG_USAGE")); //and no arguments specified + return; + }; + //Wanted input: n1 n2=n3 + char copy[1024]; + strcpy(copy,temp_line.c_str()); + //seperate section from property + const char* temp = strchr(copy,' '); + if((temp && *temp) || (temp=strchr(copy,'=')) ) copy[temp++ - copy]= 0; + else { + WriteOut(MSG_Get("PROGRAM_CONFIG_USAGE")); + return; + } + //if n1 n2 n3 then replace last space with = + const char* sign = strchr(temp,'='); + if(!sign) { + sign = strchr(temp,' '); + if(sign) { + copy[sign - copy] = '='; + } else { + //2 items specified (no space nor = between n2 and n3 + //assume that they posted: property value + //Try to determine the section. + Section* sec=control->GetSectionFromProperty(copy); + if(!sec){ + if(control->GetSectionFromProperty(temp)) return; //Weird situation:ignore + WriteOut(MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR"),copy); + return; + } //Hack to allow config ems true + char buffer[1024];strcpy(buffer,copy);strcat(buffer,"=");strcat(buffer,temp); + sign = strchr(buffer,' '); + if(sign) buffer[sign - buffer] = '='; + strcpy(copy,sec->GetName()); + temp = buffer; + } + } + + /* Input processed. Now the real job starts + * copy contains the likely "sectionname" + * temp contains "property=value" + * the section is destroyed and a new input line is given to + * the configuration parser. Then the section is restarted. + */ + char* inputline = const_cast(temp); + Section* sec = 0; + sec = control->GetSection(copy); + if(!sec) { WriteOut(MSG_Get("PROGRAM_CONFIG_SECTION_ERROR"),copy);return;} + sec->ExecuteDestroy(false); + sec->HandleInputline(inputline); + sec->ExecuteInit(false); + return; +} + + +static void CONFIG_ProgramStart(Program * * make) { + *make=new CONFIG; +} + + +void PROGRAMS_Init(Section* /*sec*/) { + /* Setup a special callback to start virtual programs */ + call_program=CALLBACK_Allocate(); + CALLBACK_Setup(call_program,&PROGRAMS_Handler,CB_RETF,"internal program"); + PROGRAMS_MakeFile("CONFIG.COM",CONFIG_ProgramStart); + + MSG_Add("PROGRAM_CONFIG_FILE_ERROR","Can't open file %s\n"); + MSG_Add("PROGRAM_CONFIG_USAGE","Config tool:\nUse -writeconf filename to write the current config.\nUse -writelang filename to write the current language strings.\n"); + MSG_Add("PROGRAM_CONFIG_SECURE_ON","Switched to secure mode.\n"); + MSG_Add("PROGRAM_CONFIG_SECURE_DISALLOW","This operation is not permitted in secure mode.\n"); + MSG_Add("PROGRAM_CONFIG_SECTION_ERROR","Section %s doesn't exist.\n"); + MSG_Add("PROGRAM_CONFIG_PROPERTY_ERROR","No such section or property.\n"); + MSG_Add("PROGRAM_CONFIG_NO_PROPERTY","There is no property %s in section %s.\n"); + MSG_Add("PROGRAM_CONFIG_GET_SYNTAX","Correct syntax: config -get \"section property\".\n"); +} diff --git a/src/misc/setup.cpp b/src/misc/setup.cpp index 00c4125..27bee4b 100644 --- a/src/misc/setup.cpp +++ b/src/misc/setup.cpp @@ -1,1003 +1,1003 @@ -/* - * Copyright (C) 2002-2009 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: setup.cpp,v 1.56 2009/05/27 09:15:42 qbix79 Exp $ */ - -#include "dosbox.h" -#include "cross.h" -#include "setup.h" -#include "control.h" -#include "support.h" -#include -#include -#include -#include -#include -#include - -using namespace std; -static std::string current_config_dir; // Set by parseconfigfile so Prop_path can use it to construct the realpath -void Value::destroy() throw(){ - if (type == V_STRING) delete _string; -} - -Value& Value::copy(Value const& in) throw(WrongType) { - if (this != &in) { //Selfassigment! - if(type != V_NONE && type != in.type) throw WrongType(); - destroy(); - plaincopy(in); - } - return *this; -} - -void Value::plaincopy(Value const& in) throw(){ - type = in.type; - _int = in._int; - _double = in._double; - _bool = in._bool; - _hex = in._hex; - if(type == V_STRING) _string = new string(*in._string); -} - -Value::operator bool () const throw(WrongType) { - if(type != V_BOOL) throw WrongType(); - return _bool; -} - -Value::operator Hex () const throw(WrongType) { - if(type != V_HEX) throw WrongType(); - return _hex; -} - -Value::operator int () const throw(WrongType) { - if(type != V_INT) throw WrongType(); - return _int; -} - -Value::operator double () const throw(WrongType) { - if(type != V_DOUBLE) throw WrongType(); - return _double; -} - -Value::operator char const* () const throw(WrongType) { - if(type != V_STRING) throw WrongType(); - return _string->c_str(); -} - -bool Value::operator==(Value const& other) { - if(this == &other) return true; - if(type != other.type) return false; - switch(type){ - case V_BOOL: - if(_bool == other._bool) return true; - break; - case V_INT: - if(_int == other._int) return true; - break; - case V_HEX: - if(_hex == other._hex) return true; - break; - case V_DOUBLE: - if(_double == other._double) return true; - break; - case V_STRING: - if((*_string) == (*other._string)) return true; - break; - default: - E_Exit("comparing stuff that doesn't make sense"); - break; - } - return false; -} -void Value::SetValue(string const& in,Etype _type) throw(WrongType) { - /* Throw exception if the current type isn't the wanted type - * Unless the wanted type is current. - */ - if(_type == V_CURRENT && type == V_NONE) throw WrongType(); - if(_type != V_CURRENT) { - if(type != V_NONE && type != _type) throw WrongType(); - type = _type; - } - switch(type){ - case V_HEX: - set_hex(in); - break; - case V_INT: - set_int(in); - break; - case V_BOOL: - set_bool(in); - break; - case V_STRING: - set_string(in); - break; - case V_DOUBLE: - set_double(in); - break; - - case V_NONE: - case V_CURRENT: - default: - /* Shouldn't happen!/Unhandled */ - throw WrongType(); - break; - } -} - -void Value::set_hex(std::string const& in) { - istringstream input(in); - input.flags(ios::hex); - int result = 0; - input >> result; - _hex = result; -} - -void Value::set_int(string const &in) { - istringstream input(in); - int result = 0; - input >> result; - _int = result; -} -void Value::set_double(string const &in) { - istringstream input(in); - double result = 0; - input >> result; - _double = result; -} - -void Value::set_bool(string const &in) { - istringstream input(in); - string result; - input >> result; - _bool = true; - lowcase(result); - /* valid false entries: 0 ,d*, f*, off everything else gets true */ - if( !result.size() ) return; - if(result[0] == '0' || result[0] == 'd' || result[0] == 'f' || result == "off") - _bool = false; -} - -void Value::set_string(string const & in) { - if(!_string) _string = new string(); - _string->assign(in); -} - -string Value::ToString() const { - ostringstream oss; - switch(type) { - case V_HEX: - oss.flags(ios::hex); - oss << _hex; - break; - case V_INT: - oss << _int; - break; - case V_BOOL: - oss << boolalpha << _bool; - break; - case V_STRING: - oss << *_string; - break; - case V_DOUBLE: - oss.precision(2); - oss << fixed << _double; - break; - case V_NONE: - case V_CURRENT: - default: - E_Exit("ToString messed up ?"); - break; - } - return oss.str(); -} - -bool Property::CheckValue(Value const& in, bool warn){ - if(suggested_values.empty()) return true; - for(iter it = suggested_values.begin();it != suggested_values.end();it++) { - if ( (*it) == in) { //Match! - return true; - } - } - if(warn) LOG_MSG("\"%s\" is not a valid value for variable: %s.\nIt might now be reset it to default value: %s",in.ToString().c_str(),propname.c_str(),default_value.ToString().c_str()); - return false; -} - -void Property::Set_help(string const& in) { - string result = string("CONFIG_") + propname; - upcase(result); - MSG_Add(result.c_str(),in.c_str()); -} - -char const* Property::Get_help() { - string result = string("CONFIG_") + propname; - upcase(result); - return MSG_Get(result.c_str()); -} - - -bool Prop_int::CheckValue(Value const& in, bool warn) { - if(suggested_values.empty() && Property::CheckValue(in,warn)) return true; - //No >= and <= in Value type and == is ambigious - int mi = min; - int ma = max; - int va = static_cast(Value(in)); - if(mi == -1 && ma == -1) return true; - if (va >= mi && va <= ma) return true; - if(warn) LOG_MSG("%s lies outside the range %s-%s for variable: %s.\nIt might now be reset to the default value: %s",in.ToString().c_str(),min.ToString().c_str(),max.ToString().c_str(),propname.c_str(),default_value.ToString().c_str()); - return false; -} - -void Prop_double::SetValue(std::string const& input){ - Value val(input,Value::V_DOUBLE); - SetVal(val,false,true); -} - -//void Property::SetValue(char* input){ -// value.SetValue(input, Value::V_CURRENT); -//} -void Prop_int::SetValue(std::string const& input){; - Value val(input,Value::V_INT); - SetVal(val,false,true); -} - -void Prop_string::SetValue(std::string const& input){ - //Special version for lowcase stuff - std::string temp(input); - //suggested values always case insensitive. - //If there are none then it can be paths and such which are case sensitive - if(!suggested_values.empty()) lowcase(temp); - Value val(temp,Value::V_STRING); - SetVal(val,false,true); -} -bool Prop_string::CheckValue(Value const& in, bool warn){ - if(suggested_values.empty()) return true; - for(iter it = suggested_values.begin();it != suggested_values.end();it++) { - if ( (*it) == in) { //Match! - return true; - } - if((*it).ToString() == "%u") { - Bitu value; - if(sscanf(in.ToString().c_str(),"%u",&value) == 1) { - return true; - } - } - } - if(warn) LOG_MSG("\"%s\" is not a valid value for variable: %s.\nIt might now be reset it to default value: %s",in.ToString().c_str(),propname.c_str(),default_value.ToString().c_str()); - return false; -} - -void Prop_path::SetValue(std::string const& input){ - //Special version to merge realpath with it - - Value val(input,Value::V_STRING); - SetVal(val,false,true); - - if(input.empty()) { - realpath = ""; - return; - } - std::string workcopy(input); - Cross::ResolveHomedir(workcopy); //Parse ~ and friends - //Prepend config directory in it exists. Check for absolute paths later - if( current_config_dir.empty()) realpath = workcopy; - else realpath = current_config_dir + CROSS_FILESPLIT + workcopy; - //Absolute paths -#if defined (WIN32) || defined(OS2) - if( workcopy.size() > 2 && workcopy[1] == ':' ) realpath = workcopy; -#else - if( workcopy.size() > 1 && workcopy[0] == '/' ) realpath = workcopy; -#endif -} - -void Prop_bool::SetValue(std::string const& input){ - value.SetValue(input,Value::V_BOOL); -} - -void Prop_hex::SetValue(std::string const& input){ - Value val(input,Value::V_HEX); - SetVal(val,false,true); -} - -void Prop_multival::make_default_value(){ - Bitu i = 1; - Property *p = section->Get_prop(0); - if(!p) return; - - std::string result = p->Get_Default_Value().ToString(); - while( (p = section->Get_prop(i++)) ) { - std::string props = p->Get_Default_Value().ToString(); - if(props == "") continue; - result += seperator; result += props; - } - Value val(result,Value::V_STRING); - SetVal(val,false,true); -} - - - -//TODO checkvalue stuff -void Prop_multival_remain::SetValue(std::string const& input) { - Value val(input,Value::V_STRING); - SetVal(val,false,true); - - std::string local(input); - int i = 0,number_of_properties = 0; - Property *p = section->Get_prop(0); - //No properties in this section. do nothing - if(!p) return; - - while( (section->Get_prop(number_of_properties)) ) - number_of_properties++; - - string::size_type loc = string::npos; - while( (p = section->Get_prop(i++)) ) { - //trim leading seperators - loc = local.find_first_not_of(seperator); - if(loc != string::npos) local.erase(0,loc); - loc = local.find_first_of(seperator); - string in = "";//default value - /* when i == number_of_properties add the total line. (makes more then - * one string argument possible for parameters of cpu) */ - if(loc != string::npos && i < number_of_properties) { //seperator found - in = local.substr(0,loc); - local.erase(0,loc+1); - } else if(local.size()) { //last argument or last property - in = local; - local = ""; - } - //Test Value. If it fails set default - Value valtest (in,p->Get_type()); - if(!p->CheckValue(valtest,true)) { - make_default_value(); - return; - } - p->SetValue(in); - } -} - -//TODO checkvalue stuff -void Prop_multival::SetValue(std::string const& input) { - Value val(input,Value::V_STRING); - SetVal(val,false,true); - - std::string local(input); - int i = 0; - Property *p = section->Get_prop(0); - //No properties in this section. do nothing - if(!p) return; - string::size_type loc = string::npos; - while( (p = section->Get_prop(i++)) ) { - //trim leading seperators - loc = local.find_first_not_of(seperator); - if(loc != string::npos) local.erase(0,loc); - loc = local.find_first_of(seperator); - string in = "";//default value - if(loc != string::npos) { //seperator found - in = local.substr(0,loc); - local.erase(0,loc+1); - } else if(local.size()) { //last argument - in = local; - local = ""; - } - //Test Value. If it fails set default - Value valtest (in,p->Get_type()); - if(!p->CheckValue(valtest,true)) { - make_default_value(); - return; - } - p->SetValue(in); - - } -} - -const std::vector& Property::GetValues() const { - return suggested_values; -} -const std::vector& Prop_multival::GetValues() const -{ - Property *p = section->Get_prop(0); - //No properties in this section. do nothing - if(!p) return suggested_values; - int i =0; - while( (p = section->Get_prop(i++)) ) { - std::vector v = p->GetValues(); - if(!v.empty()) return p->GetValues(); - } - return suggested_values; -} - -/* -void Section_prop::Add_double(char const * const _propname, double _value) { - Property* test=new Prop_double(_propname,_value); - properties.push_back(test); -}*/ - -void Property::Set_values(const char * const *in) { - Value::Etype type = default_value.type; - int i = 0; - while (in[i]) { - Value val(in[i],type); - suggested_values.push_back(val); - i++; - } -} - -Prop_int* Section_prop::Add_int(string const& _propname, Property::Changeable::Value when, int _value) { - Prop_int* test=new Prop_int(_propname,when,_value); - properties.push_back(test); - return test; -} - -Prop_string* Section_prop::Add_string(string const& _propname, Property::Changeable::Value when, char const * const _value) { - Prop_string* test=new Prop_string(_propname,when,_value); - properties.push_back(test); - return test; -} - -Prop_path* Section_prop::Add_path(string const& _propname, Property::Changeable::Value when, char const * const _value) { - Prop_path* test=new Prop_path(_propname,when,_value); - properties.push_back(test); - return test; -} - -Prop_bool* Section_prop::Add_bool(string const& _propname, Property::Changeable::Value when, bool _value) { - Prop_bool* test=new Prop_bool(_propname,when,_value); - properties.push_back(test); - return test; -} -Prop_hex* Section_prop::Add_hex(string const& _propname, Property::Changeable::Value when, Hex _value) { - Prop_hex* test=new Prop_hex(_propname,when,_value); - properties.push_back(test); - return test; -} -Prop_multival* Section_prop::Add_multi(std::string const& _propname, Property::Changeable::Value when,std::string const& sep) { - Prop_multival* test = new Prop_multival(_propname,when,sep); - properties.push_back(test); - return test; -} -Prop_multival_remain* Section_prop::Add_multiremain(std::string const& _propname, Property::Changeable::Value when,std::string const& sep) { - Prop_multival_remain* test = new Prop_multival_remain(_propname,when,sep); - properties.push_back(test); - return test; -} - -int Section_prop::Get_int(string const&_propname) const { - for(const_it tel=properties.begin();tel!=properties.end();tel++){ - if((*tel)->propname==_propname){ - return ((*tel)->GetValue()); - } - } - return 0; -} - -bool Section_prop::Get_bool(string const& _propname) const { - for(const_it tel=properties.begin();tel!=properties.end();tel++){ - if((*tel)->propname==_propname){ - return ((*tel)->GetValue()); - } - } - return false; -} -double Section_prop::Get_double(string const& _propname) const { - for(const_it tel=properties.begin();tel!=properties.end();tel++){ - if((*tel)->propname==_propname){ - return ((*tel)->GetValue()); - } - } - return 0.0; -} - -Prop_path* Section_prop::Get_path(string const& _propname) const { - for(const_it tel=properties.begin();tel!=properties.end();tel++){ - if((*tel)->propname==_propname){ - Prop_path* val = dynamic_cast((*tel)); - if(val) return val; else return NULL; - } - } - return NULL; -} - -Prop_multival* Section_prop::Get_multival(string const& _propname) const { - for(const_it tel=properties.begin();tel!=properties.end();tel++){ - if((*tel)->propname==_propname){ - Prop_multival* val = dynamic_cast((*tel)); - if(val) return val; else return NULL; - } - } - return NULL; -} - -Prop_multival_remain* Section_prop::Get_multivalremain(string const& _propname) const { - for(const_it tel=properties.begin();tel!=properties.end();tel++){ - if((*tel)->propname==_propname){ - Prop_multival_remain* val = dynamic_cast((*tel)); - if(val) return val; else return NULL; - } - } - return NULL; -} -Property* Section_prop::Get_prop(int index){ - for(it tel=properties.begin();tel!=properties.end();tel++){ - if(!index--) return (*tel); - } - return NULL; -} - -const char* Section_prop::Get_string(string const& _propname) const { - for(const_it tel=properties.begin();tel!=properties.end();tel++){ - if((*tel)->propname==_propname){ - return ((*tel)->GetValue()); - } - } - return ""; -} -Hex Section_prop::Get_hex(string const& _propname) const { - for(const_it tel=properties.begin();tel!=properties.end();tel++){ - if((*tel)->propname==_propname){ - return ((*tel)->GetValue()); - } - } - return 0; -} - -void trim(string& in) { - string::size_type loc = in.find_first_not_of(" \r\t\f\n"); - if(loc != string::npos) in.erase(0,loc); - loc = in.find_last_not_of(" \r\t\f\n"); - if(loc != string::npos) in.erase(loc+1); -} - -//TODO double c_str -void Section_prop::HandleInputline(string const& gegevens){ - string str1 = gegevens; - string::size_type loc = str1.find('='); - if(loc == string::npos) return; - string name = str1.substr(0,loc); - string val = str1.substr(loc + 1); - /* trim the results incase there were spaces somewhere */ - trim(name);trim(val); - for(it tel=properties.begin();tel!=properties.end();tel++){ - if(!strcasecmp((*tel)->propname.c_str(),name.c_str())){ - (*tel)->SetValue(val); - return; - } - } -} - -void Section_prop::PrintData(FILE* outfile) const { - /* Now print out the individual section entries */ - for(const_it tel=properties.begin();tel!=properties.end();tel++){ - fprintf(outfile,"%s=%s\n",(*tel)->propname.c_str(),(*tel)->GetValue().ToString().c_str()); - } -} - -//TODO geen noodzaak voor 2 keer c_str -string Section_prop::GetPropValue(string const& _property) const{ - for(const_it tel=properties.begin();tel!=properties.end();tel++){ - if(!strcasecmp((*tel)->propname.c_str(),_property.c_str())){ - return (*tel)->GetValue().ToString(); - } - } - return NO_SUCH_PROPERTY; -} - -void Section_line::HandleInputline(string const& line){ - data+=line; - data+="\n"; -} - -void Section_line::PrintData(FILE* outfile) const { - fprintf(outfile,"%s",data.c_str()); -} - -string Section_line::GetPropValue(string const& /* _property*/) const { - return NO_SUCH_PROPERTY; -} - -bool Config::PrintConfig(char const * const configfilename) const { - char temp[50];char helpline[256]; - FILE* outfile=fopen(configfilename,"w+t"); - if(outfile==NULL) return false; - - /* Print start of configfile and add an return to improve readibility. */ - fprintf(outfile,MSG_Get("CONFIGFILE_INTRO"),VERSION); - fprintf(outfile,"\n"); - for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){ - /* Print out the Section header */ - Section_prop *sec = dynamic_cast(*tel); - strcpy(temp,(*tel)->GetName()); - lowcase(temp); - fprintf(outfile,"[%s]\n",temp); - - if (sec) { - Property *p; - size_t i = 0, maxwidth = 0; - while ((p = sec->Get_prop(i++))) { - size_t w = strlen(p->propname.c_str()); - if (w > maxwidth) maxwidth = w; - } - i=0; - char prefix[80]; - snprintf(prefix,80, "\n# %*s ", maxwidth, ""); - while ((p = sec->Get_prop(i++))) { - std::string help = p->Get_help(); - std::string::size_type pos = std::string::npos; - while ((pos = help.find("\n", pos+1)) != std::string::npos) { - help.replace(pos, 1, prefix); - } - - fprintf(outfile, "# %*s: %s", maxwidth, p->propname.c_str(), help.c_str()); - - std::vector values = p->GetValues(); - if (!values.empty()) { - fprintf(outfile, "%s%s:", prefix, MSG_Get("CONFIG_SUGGESTED_VALUES")); - std::vector::iterator it = values.begin(); - while (it != values.end()) { - if((*it).ToString() != "%u") { //Hack hack hack. else we need to modify GetValues, but that one is const... - if (it != values.begin()) fputs(",", outfile); - fprintf(outfile, " %s", (*it).ToString().c_str()); - } - ++it; - } - fprintf(outfile,"."); - } - fprintf(outfile, "\n"); - } - } else { - upcase(temp); - strcat(temp,"_CONFIGFILE_HELP"); - const char * helpstr=MSG_Get(temp); - char * helpwrite=helpline; - while (*helpstr) { - *helpwrite++=*helpstr; - if (*helpstr == '\n') { - *helpwrite=0; - fprintf(outfile,"# %s",helpline); - helpwrite=helpline; - } - helpstr++; - } - } - - fprintf(outfile,"\n"); - (*tel)->PrintData(outfile); - fprintf(outfile,"\n"); /* Always an empty line between sections */ - } - fclose(outfile); - return true; -} - - -Section_prop* Config::AddSection_prop(char const * const _name,void (*_initfunction)(Section*),bool canchange){ - Section_prop* blah = new Section_prop(_name); - blah->AddInitFunction(_initfunction,canchange); - sectionlist.push_back(blah); - return blah; -} - -Section_prop::~Section_prop() { -//ExecuteDestroy should be here else the destroy functions use destroyed properties - ExecuteDestroy(true); - /* Delete properties themself (properties stores the pointer of a prop */ - for(it prop = properties.begin(); prop != properties.end(); prop++) - delete (*prop); -} - - -Section_line* Config::AddSection_line(char const * const _name,void (*_initfunction)(Section*)){ - Section_line* blah = new Section_line(_name); - blah->AddInitFunction(_initfunction); - sectionlist.push_back(blah); - return blah; -} - - -void Config::Init() { - for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){ - (*tel)->ExecuteInit(); - } -} - -void Section::AddInitFunction(SectionFunction func,bool canchange) { - initfunctions.push_back(Function_wrapper(func,canchange)); -} - -void Section::AddDestroyFunction(SectionFunction func,bool canchange) { - destroyfunctions.push_front(Function_wrapper(func,canchange)); -} - - -void Section::ExecuteInit(bool initall) { - typedef std::list::iterator func_it; - for (func_it tel=initfunctions.begin(); tel!=initfunctions.end(); tel++) { - if(initall || (*tel).canchange) (*tel).function(this); - } -} - -void Section::ExecuteDestroy(bool destroyall) { - typedef std::list::iterator func_it; - for (func_it tel=destroyfunctions.begin(); tel!=destroyfunctions.end(); ) { - if(destroyall || (*tel).canchange) { - (*tel).function(this); - tel=destroyfunctions.erase(tel); //Remove destroyfunction once used - } else tel++; - } -} - -Config::~Config() { - reverse_it cnt=sectionlist.rbegin(); - while (cnt!=sectionlist.rend()) { - delete (*cnt); - cnt++; - } -} - -Section* Config::GetSection(int index){ - for (it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){ - if (!index--) return (*tel); - } - return NULL; -} -//c_str() 2x -Section* Config::GetSection(string const& _sectionname) const{ - for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){ - if (!strcasecmp((*tel)->GetName(),_sectionname.c_str())) return (*tel); - } - return NULL; -} - -Section* Config::GetSectionFromProperty(char const * const prop) const{ - for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){ - if ((*tel)->GetPropValue(prop) != NO_SUCH_PROPERTY) return (*tel); - } - return NULL; -} - - -bool Config::ParseConfigFile(char const * const configfilename){ - static bool first_configfile = true; - ifstream in(configfilename); - if (!in) return false; - const char * settings_type = first_configfile?"primary":"additional"; - first_configfile = false; - LOG_MSG("CONFIG:Loading %s settings from config file %s", settings_type,configfilename); - - //Get directory from configfilename, used with relative paths. - current_config_dir=configfilename; - std::string::size_type pos = current_config_dir.rfind(CROSS_FILESPLIT); - if(pos == std::string::npos) pos = 0; //No directory then erase string - current_config_dir.erase(pos); - - string gegevens; - Section* currentsection = NULL; - Section* testsec = NULL; - while (getline(in,gegevens)) { - - /* strip leading/trailing whitespace */ - trim(gegevens); - if(!gegevens.size()) continue; - - switch(gegevens[0]){ - case '%': - case '\0': - case '#': - case ' ': - case '\n': - continue; - break; - case '[': - { - string::size_type loc = gegevens.find(']'); - if(loc == string::npos) continue; - gegevens.erase(loc); - testsec = GetSection(gegevens.substr(1)); - if(testsec != NULL ) currentsection = testsec; - testsec = NULL; - } - break; - default: - try { - if(currentsection) currentsection->HandleInputline(gegevens); - } catch(const char* message) { - message=0; - //EXIT with message - } - break; - } - } - current_config_dir.clear();//So internal changes don't use the path information - return true; -} - -void Config::ParseEnv(char ** envp) { - for(char** env=envp; *env;env++) { - char copy[1024]; - safe_strncpy(copy,*env,1024); - if(strncasecmp(copy,"DOSBOX_",7)) - continue; - char* sec_name = ©[7]; - if(!(*sec_name)) - continue; - char* prop_name = strrchr(sec_name,'_'); - if(!prop_name || !(*prop_name)) - continue; - *prop_name++=0; - Section* sect = GetSection(sec_name); - if(!sect) - continue; - sect->HandleInputline(prop_name); - } -} - -void Config::SetStartUp(void (*_function)(void)) { - _start_function=_function; -} - - -void Config::StartUp(void) { - (*_start_function)(); -} - -bool CommandLine::FindExist(char const * const name,bool remove) { - cmd_it it; - if (!(FindEntry(name,it,false))) return false; - if (remove) cmds.erase(it); - return true; -} - -bool CommandLine::FindHex(char const * const name,int & value,bool remove) { - cmd_it it,it_next; - if (!(FindEntry(name,it,true))) return false; - it_next=it;it_next++; - sscanf((*it_next).c_str(),"%X",&value); - if (remove) cmds.erase(it,++it_next); - return true; -} - -bool CommandLine::FindInt(char const * const name,int & value,bool remove) { - cmd_it it,it_next; - if (!(FindEntry(name,it,true))) return false; - it_next=it;it_next++; - value=atoi((*it_next).c_str()); - if (remove) cmds.erase(it,++it_next); - return true; -} - -bool CommandLine::FindString(char const * const name,std::string & value,bool remove) { - cmd_it it,it_next; - if (!(FindEntry(name,it,true))) return false; - it_next=it;it_next++; - value=*it_next; - if (remove) cmds.erase(it,++it_next); - return true; -} - -bool CommandLine::FindCommand(unsigned int which,std::string & value) { - if (which<1) return false; - if (which>cmds.size()) return false; - cmd_it it=cmds.begin(); - for (;which>1;which--) it++; - value=(*it); - return true; -} - -bool CommandLine::FindEntry(char const * const name,cmd_it & it,bool neednext) { - for (it=cmds.begin();it!=cmds.end();it++) { - if (!strcasecmp((*it).c_str(),name)) { - cmd_it itnext=it;itnext++; - if (neednext && (itnext==cmds.end())) return false; - return true; - } - } - return false; -} - -bool CommandLine::FindStringBegin(char const* const begin,std::string & value, bool remove) { - size_t len = strlen(begin); - for (cmd_it it=cmds.begin();it!=cmds.end();it++) { - if (strncmp(begin,(*it).c_str(),len)==0) { - value=((*it).c_str() + len); - if (remove) cmds.erase(it); - return true; - } - } - return false; -} - -bool CommandLine::FindStringRemain(char const * const name,std::string & value) { - cmd_it it;value=""; - if (!FindEntry(name,it)) return false; - it++; - for (;it!=cmds.end();it++) { - value+=" "; - value+=(*it); - } - return true; -} - -bool CommandLine::GetStringRemain(std::string & value) { - if(!cmds.size()) return false; - - cmd_it it=cmds.begin();value=(*it++); - for(;it != cmds.end();it++) { - value+=" "; - value+=(*it); - } - return true; -} - - -unsigned int CommandLine::GetCount(void) { - return (unsigned int)cmds.size(); -} - -CommandLine::CommandLine(int argc,char const * const argv[]) { - if (argc>0) { - file_name=argv[0]; - } - int i=1; - while (i +#include +#include +#include +#include +#include + +using namespace std; +static std::string current_config_dir; // Set by parseconfigfile so Prop_path can use it to construct the realpath +void Value::destroy() throw(){ + if (type == V_STRING) delete _string; +} + +Value& Value::copy(Value const& in) throw(WrongType) { + if (this != &in) { //Selfassigment! + if(type != V_NONE && type != in.type) throw WrongType(); + destroy(); + plaincopy(in); + } + return *this; +} + +void Value::plaincopy(Value const& in) throw(){ + type = in.type; + _int = in._int; + _double = in._double; + _bool = in._bool; + _hex = in._hex; + if(type == V_STRING) _string = new string(*in._string); +} + +Value::operator bool () const throw(WrongType) { + if(type != V_BOOL) throw WrongType(); + return _bool; +} + +Value::operator Hex () const throw(WrongType) { + if(type != V_HEX) throw WrongType(); + return _hex; +} + +Value::operator int () const throw(WrongType) { + if(type != V_INT) throw WrongType(); + return _int; +} + +Value::operator double () const throw(WrongType) { + if(type != V_DOUBLE) throw WrongType(); + return _double; +} + +Value::operator char const* () const throw(WrongType) { + if(type != V_STRING) throw WrongType(); + return _string->c_str(); +} + +bool Value::operator==(Value const& other) { + if(this == &other) return true; + if(type != other.type) return false; + switch(type){ + case V_BOOL: + if(_bool == other._bool) return true; + break; + case V_INT: + if(_int == other._int) return true; + break; + case V_HEX: + if(_hex == other._hex) return true; + break; + case V_DOUBLE: + if(_double == other._double) return true; + break; + case V_STRING: + if((*_string) == (*other._string)) return true; + break; + default: + E_Exit("comparing stuff that doesn't make sense"); + break; + } + return false; +} +void Value::SetValue(string const& in,Etype _type) throw(WrongType) { + /* Throw exception if the current type isn't the wanted type + * Unless the wanted type is current. + */ + if(_type == V_CURRENT && type == V_NONE) throw WrongType(); + if(_type != V_CURRENT) { + if(type != V_NONE && type != _type) throw WrongType(); + type = _type; + } + switch(type){ + case V_HEX: + set_hex(in); + break; + case V_INT: + set_int(in); + break; + case V_BOOL: + set_bool(in); + break; + case V_STRING: + set_string(in); + break; + case V_DOUBLE: + set_double(in); + break; + + case V_NONE: + case V_CURRENT: + default: + /* Shouldn't happen!/Unhandled */ + throw WrongType(); + break; + } +} + +void Value::set_hex(std::string const& in) { + istringstream input(in); + input.flags(ios::hex); + int result = 0; + input >> result; + _hex = result; +} + +void Value::set_int(string const &in) { + istringstream input(in); + int result = 0; + input >> result; + _int = result; +} +void Value::set_double(string const &in) { + istringstream input(in); + double result = 0; + input >> result; + _double = result; +} + +void Value::set_bool(string const &in) { + istringstream input(in); + string result; + input >> result; + _bool = true; + lowcase(result); + /* valid false entries: 0 ,d*, f*, off everything else gets true */ + if( !result.size() ) return; + if(result[0] == '0' || result[0] == 'd' || result[0] == 'f' || result == "off") + _bool = false; +} + +void Value::set_string(string const & in) { + if(!_string) _string = new string(); + _string->assign(in); +} + +string Value::ToString() const { + ostringstream oss; + switch(type) { + case V_HEX: + oss.flags(ios::hex); + oss << _hex; + break; + case V_INT: + oss << _int; + break; + case V_BOOL: + oss << boolalpha << _bool; + break; + case V_STRING: + oss << *_string; + break; + case V_DOUBLE: + oss.precision(2); + oss << fixed << _double; + break; + case V_NONE: + case V_CURRENT: + default: + E_Exit("ToString messed up ?"); + break; + } + return oss.str(); +} + +bool Property::CheckValue(Value const& in, bool warn){ + if(suggested_values.empty()) return true; + for(iter it = suggested_values.begin();it != suggested_values.end();it++) { + if ( (*it) == in) { //Match! + return true; + } + } + if(warn) LOG_MSG("\"%s\" is not a valid value for variable: %s.\nIt might now be reset it to default value: %s",in.ToString().c_str(),propname.c_str(),default_value.ToString().c_str()); + return false; +} + +void Property::Set_help(string const& in) { + string result = string("CONFIG_") + propname; + upcase(result); + MSG_Add(result.c_str(),in.c_str()); +} + +char const* Property::Get_help() { + string result = string("CONFIG_") + propname; + upcase(result); + return MSG_Get(result.c_str()); +} + + +bool Prop_int::CheckValue(Value const& in, bool warn) { + if(suggested_values.empty() && Property::CheckValue(in,warn)) return true; + //No >= and <= in Value type and == is ambigious + int mi = min; + int ma = max; + int va = static_cast(Value(in)); + if(mi == -1 && ma == -1) return true; + if (va >= mi && va <= ma) return true; + if(warn) LOG_MSG("%s lies outside the range %s-%s for variable: %s.\nIt might now be reset to the default value: %s",in.ToString().c_str(),min.ToString().c_str(),max.ToString().c_str(),propname.c_str(),default_value.ToString().c_str()); + return false; +} + +void Prop_double::SetValue(std::string const& input){ + Value val(input,Value::V_DOUBLE); + SetVal(val,false,true); +} + +//void Property::SetValue(char* input){ +// value.SetValue(input, Value::V_CURRENT); +//} +void Prop_int::SetValue(std::string const& input){; + Value val(input,Value::V_INT); + SetVal(val,false,true); +} + +void Prop_string::SetValue(std::string const& input){ + //Special version for lowcase stuff + std::string temp(input); + //suggested values always case insensitive. + //If there are none then it can be paths and such which are case sensitive + if(!suggested_values.empty()) lowcase(temp); + Value val(temp,Value::V_STRING); + SetVal(val,false,true); +} +bool Prop_string::CheckValue(Value const& in, bool warn){ + if(suggested_values.empty()) return true; + for(iter it = suggested_values.begin();it != suggested_values.end();it++) { + if ( (*it) == in) { //Match! + return true; + } + if((*it).ToString() == "%u") { + Bitu value; + if(sscanf(in.ToString().c_str(),"%u",&value) == 1) { + return true; + } + } + } + if(warn) LOG_MSG("\"%s\" is not a valid value for variable: %s.\nIt might now be reset it to default value: %s",in.ToString().c_str(),propname.c_str(),default_value.ToString().c_str()); + return false; +} + +void Prop_path::SetValue(std::string const& input){ + //Special version to merge realpath with it + + Value val(input,Value::V_STRING); + SetVal(val,false,true); + + if(input.empty()) { + realpath = ""; + return; + } + std::string workcopy(input); + Cross::ResolveHomedir(workcopy); //Parse ~ and friends + //Prepend config directory in it exists. Check for absolute paths later + if( current_config_dir.empty()) realpath = workcopy; + else realpath = current_config_dir + CROSS_FILESPLIT + workcopy; + //Absolute paths +#if defined (WIN32) || defined(OS2) + if( workcopy.size() > 2 && workcopy[1] == ':' ) realpath = workcopy; +#else + if( workcopy.size() > 1 && workcopy[0] == '/' ) realpath = workcopy; +#endif +} + +void Prop_bool::SetValue(std::string const& input){ + value.SetValue(input,Value::V_BOOL); +} + +void Prop_hex::SetValue(std::string const& input){ + Value val(input,Value::V_HEX); + SetVal(val,false,true); +} + +void Prop_multival::make_default_value(){ + Bitu i = 1; + Property *p = section->Get_prop(0); + if(!p) return; + + std::string result = p->Get_Default_Value().ToString(); + while( (p = section->Get_prop(i++)) ) { + std::string props = p->Get_Default_Value().ToString(); + if(props == "") continue; + result += seperator; result += props; + } + Value val(result,Value::V_STRING); + SetVal(val,false,true); +} + + + +//TODO checkvalue stuff +void Prop_multival_remain::SetValue(std::string const& input) { + Value val(input,Value::V_STRING); + SetVal(val,false,true); + + std::string local(input); + int i = 0,number_of_properties = 0; + Property *p = section->Get_prop(0); + //No properties in this section. do nothing + if(!p) return; + + while( (section->Get_prop(number_of_properties)) ) + number_of_properties++; + + string::size_type loc = string::npos; + while( (p = section->Get_prop(i++)) ) { + //trim leading seperators + loc = local.find_first_not_of(seperator); + if(loc != string::npos) local.erase(0,loc); + loc = local.find_first_of(seperator); + string in = "";//default value + /* when i == number_of_properties add the total line. (makes more then + * one string argument possible for parameters of cpu) */ + if(loc != string::npos && i < number_of_properties) { //seperator found + in = local.substr(0,loc); + local.erase(0,loc+1); + } else if(local.size()) { //last argument or last property + in = local; + local = ""; + } + //Test Value. If it fails set default + Value valtest (in,p->Get_type()); + if(!p->CheckValue(valtest,true)) { + make_default_value(); + return; + } + p->SetValue(in); + } +} + +//TODO checkvalue stuff +void Prop_multival::SetValue(std::string const& input) { + Value val(input,Value::V_STRING); + SetVal(val,false,true); + + std::string local(input); + int i = 0; + Property *p = section->Get_prop(0); + //No properties in this section. do nothing + if(!p) return; + string::size_type loc = string::npos; + while( (p = section->Get_prop(i++)) ) { + //trim leading seperators + loc = local.find_first_not_of(seperator); + if(loc != string::npos) local.erase(0,loc); + loc = local.find_first_of(seperator); + string in = "";//default value + if(loc != string::npos) { //seperator found + in = local.substr(0,loc); + local.erase(0,loc+1); + } else if(local.size()) { //last argument + in = local; + local = ""; + } + //Test Value. If it fails set default + Value valtest (in,p->Get_type()); + if(!p->CheckValue(valtest,true)) { + make_default_value(); + return; + } + p->SetValue(in); + + } +} + +const std::vector& Property::GetValues() const { + return suggested_values; +} +const std::vector& Prop_multival::GetValues() const +{ + Property *p = section->Get_prop(0); + //No properties in this section. do nothing + if(!p) return suggested_values; + int i =0; + while( (p = section->Get_prop(i++)) ) { + std::vector v = p->GetValues(); + if(!v.empty()) return p->GetValues(); + } + return suggested_values; +} + +/* +void Section_prop::Add_double(char const * const _propname, double _value) { + Property* test=new Prop_double(_propname,_value); + properties.push_back(test); +}*/ + +void Property::Set_values(const char * const *in) { + Value::Etype type = default_value.type; + int i = 0; + while (in[i]) { + Value val(in[i],type); + suggested_values.push_back(val); + i++; + } +} + +Prop_int* Section_prop::Add_int(string const& _propname, Property::Changeable::Value when, int _value) { + Prop_int* test=new Prop_int(_propname,when,_value); + properties.push_back(test); + return test; +} + +Prop_string* Section_prop::Add_string(string const& _propname, Property::Changeable::Value when, char const * const _value) { + Prop_string* test=new Prop_string(_propname,when,_value); + properties.push_back(test); + return test; +} + +Prop_path* Section_prop::Add_path(string const& _propname, Property::Changeable::Value when, char const * const _value) { + Prop_path* test=new Prop_path(_propname,when,_value); + properties.push_back(test); + return test; +} + +Prop_bool* Section_prop::Add_bool(string const& _propname, Property::Changeable::Value when, bool _value) { + Prop_bool* test=new Prop_bool(_propname,when,_value); + properties.push_back(test); + return test; +} +Prop_hex* Section_prop::Add_hex(string const& _propname, Property::Changeable::Value when, Hex _value) { + Prop_hex* test=new Prop_hex(_propname,when,_value); + properties.push_back(test); + return test; +} +Prop_multival* Section_prop::Add_multi(std::string const& _propname, Property::Changeable::Value when,std::string const& sep) { + Prop_multival* test = new Prop_multival(_propname,when,sep); + properties.push_back(test); + return test; +} +Prop_multival_remain* Section_prop::Add_multiremain(std::string const& _propname, Property::Changeable::Value when,std::string const& sep) { + Prop_multival_remain* test = new Prop_multival_remain(_propname,when,sep); + properties.push_back(test); + return test; +} + +int Section_prop::Get_int(string const&_propname) const { + for(const_it tel=properties.begin();tel!=properties.end();tel++){ + if((*tel)->propname==_propname){ + return ((*tel)->GetValue()); + } + } + return 0; +} + +bool Section_prop::Get_bool(string const& _propname) const { + for(const_it tel=properties.begin();tel!=properties.end();tel++){ + if((*tel)->propname==_propname){ + return ((*tel)->GetValue()); + } + } + return false; +} +double Section_prop::Get_double(string const& _propname) const { + for(const_it tel=properties.begin();tel!=properties.end();tel++){ + if((*tel)->propname==_propname){ + return ((*tel)->GetValue()); + } + } + return 0.0; +} + +Prop_path* Section_prop::Get_path(string const& _propname) const { + for(const_it tel=properties.begin();tel!=properties.end();tel++){ + if((*tel)->propname==_propname){ + Prop_path* val = dynamic_cast((*tel)); + if(val) return val; else return NULL; + } + } + return NULL; +} + +Prop_multival* Section_prop::Get_multival(string const& _propname) const { + for(const_it tel=properties.begin();tel!=properties.end();tel++){ + if((*tel)->propname==_propname){ + Prop_multival* val = dynamic_cast((*tel)); + if(val) return val; else return NULL; + } + } + return NULL; +} + +Prop_multival_remain* Section_prop::Get_multivalremain(string const& _propname) const { + for(const_it tel=properties.begin();tel!=properties.end();tel++){ + if((*tel)->propname==_propname){ + Prop_multival_remain* val = dynamic_cast((*tel)); + if(val) return val; else return NULL; + } + } + return NULL; +} +Property* Section_prop::Get_prop(int index){ + for(it tel=properties.begin();tel!=properties.end();tel++){ + if(!index--) return (*tel); + } + return NULL; +} + +const char* Section_prop::Get_string(string const& _propname) const { + for(const_it tel=properties.begin();tel!=properties.end();tel++){ + if((*tel)->propname==_propname){ + return ((*tel)->GetValue()); + } + } + return ""; +} +Hex Section_prop::Get_hex(string const& _propname) const { + for(const_it tel=properties.begin();tel!=properties.end();tel++){ + if((*tel)->propname==_propname){ + return ((*tel)->GetValue()); + } + } + return 0; +} + +void trim(string& in) { + string::size_type loc = in.find_first_not_of(" \r\t\f\n"); + if(loc != string::npos) in.erase(0,loc); + loc = in.find_last_not_of(" \r\t\f\n"); + if(loc != string::npos) in.erase(loc+1); +} + +//TODO double c_str +void Section_prop::HandleInputline(string const& gegevens){ + string str1 = gegevens; + string::size_type loc = str1.find('='); + if(loc == string::npos) return; + string name = str1.substr(0,loc); + string val = str1.substr(loc + 1); + /* trim the results incase there were spaces somewhere */ + trim(name);trim(val); + for(it tel=properties.begin();tel!=properties.end();tel++){ + if(!strcasecmp((*tel)->propname.c_str(),name.c_str())){ + (*tel)->SetValue(val); + return; + } + } +} + +void Section_prop::PrintData(FILE* outfile) const { + /* Now print out the individual section entries */ + for(const_it tel=properties.begin();tel!=properties.end();tel++){ + fprintf(outfile,"%s=%s\n",(*tel)->propname.c_str(),(*tel)->GetValue().ToString().c_str()); + } +} + +//TODO geen noodzaak voor 2 keer c_str +string Section_prop::GetPropValue(string const& _property) const{ + for(const_it tel=properties.begin();tel!=properties.end();tel++){ + if(!strcasecmp((*tel)->propname.c_str(),_property.c_str())){ + return (*tel)->GetValue().ToString(); + } + } + return NO_SUCH_PROPERTY; +} + +void Section_line::HandleInputline(string const& line){ + data+=line; + data+="\n"; +} + +void Section_line::PrintData(FILE* outfile) const { + fprintf(outfile,"%s",data.c_str()); +} + +string Section_line::GetPropValue(string const& /* _property*/) const { + return NO_SUCH_PROPERTY; +} + +bool Config::PrintConfig(char const * const configfilename) const { + char temp[50];char helpline[256]; + FILE* outfile=fopen(configfilename,"w+t"); + if(outfile==NULL) return false; + + /* Print start of configfile and add an return to improve readibility. */ + fprintf(outfile,MSG_Get("CONFIGFILE_INTRO"),VERSION); + fprintf(outfile,"\n"); + for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){ + /* Print out the Section header */ + Section_prop *sec = dynamic_cast(*tel); + strcpy(temp,(*tel)->GetName()); + lowcase(temp); + fprintf(outfile,"[%s]\n",temp); + + if (sec) { + Property *p; + size_t i = 0, maxwidth = 0; + while ((p = sec->Get_prop(i++))) { + size_t w = strlen(p->propname.c_str()); + if (w > maxwidth) maxwidth = w; + } + i=0; + char prefix[80]; + snprintf(prefix,80, "\n# %*s ", maxwidth, ""); + while ((p = sec->Get_prop(i++))) { + std::string help = p->Get_help(); + std::string::size_type pos = std::string::npos; + while ((pos = help.find("\n", pos+1)) != std::string::npos) { + help.replace(pos, 1, prefix); + } + + fprintf(outfile, "# %*s: %s", maxwidth, p->propname.c_str(), help.c_str()); + + std::vector values = p->GetValues(); + if (!values.empty()) { + fprintf(outfile, "%s%s:", prefix, MSG_Get("CONFIG_SUGGESTED_VALUES")); + std::vector::iterator it = values.begin(); + while (it != values.end()) { + if((*it).ToString() != "%u") { //Hack hack hack. else we need to modify GetValues, but that one is const... + if (it != values.begin()) fputs(",", outfile); + fprintf(outfile, " %s", (*it).ToString().c_str()); + } + ++it; + } + fprintf(outfile,"."); + } + fprintf(outfile, "\n"); + } + } else { + upcase(temp); + strcat(temp,"_CONFIGFILE_HELP"); + const char * helpstr=MSG_Get(temp); + char * helpwrite=helpline; + while (*helpstr) { + *helpwrite++=*helpstr; + if (*helpstr == '\n') { + *helpwrite=0; + fprintf(outfile,"# %s",helpline); + helpwrite=helpline; + } + helpstr++; + } + } + + fprintf(outfile,"\n"); + (*tel)->PrintData(outfile); + fprintf(outfile,"\n"); /* Always an empty line between sections */ + } + fclose(outfile); + return true; +} + + +Section_prop* Config::AddSection_prop(char const * const _name,void (*_initfunction)(Section*),bool canchange){ + Section_prop* blah = new Section_prop(_name); + blah->AddInitFunction(_initfunction,canchange); + sectionlist.push_back(blah); + return blah; +} + +Section_prop::~Section_prop() { +//ExecuteDestroy should be here else the destroy functions use destroyed properties + ExecuteDestroy(true); + /* Delete properties themself (properties stores the pointer of a prop */ + for(it prop = properties.begin(); prop != properties.end(); prop++) + delete (*prop); +} + + +Section_line* Config::AddSection_line(char const * const _name,void (*_initfunction)(Section*)){ + Section_line* blah = new Section_line(_name); + blah->AddInitFunction(_initfunction); + sectionlist.push_back(blah); + return blah; +} + + +void Config::Init() { + for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){ + (*tel)->ExecuteInit(); + } +} + +void Section::AddInitFunction(SectionFunction func,bool canchange) { + initfunctions.push_back(Function_wrapper(func,canchange)); +} + +void Section::AddDestroyFunction(SectionFunction func,bool canchange) { + destroyfunctions.push_front(Function_wrapper(func,canchange)); +} + + +void Section::ExecuteInit(bool initall) { + typedef std::list::iterator func_it; + for (func_it tel=initfunctions.begin(); tel!=initfunctions.end(); tel++) { + if(initall || (*tel).canchange) (*tel).function(this); + } +} + +void Section::ExecuteDestroy(bool destroyall) { + typedef std::list::iterator func_it; + for (func_it tel=destroyfunctions.begin(); tel!=destroyfunctions.end(); ) { + if(destroyall || (*tel).canchange) { + (*tel).function(this); + tel=destroyfunctions.erase(tel); //Remove destroyfunction once used + } else tel++; + } +} + +Config::~Config() { + reverse_it cnt=sectionlist.rbegin(); + while (cnt!=sectionlist.rend()) { + delete (*cnt); + cnt++; + } +} + +Section* Config::GetSection(int index){ + for (it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){ + if (!index--) return (*tel); + } + return NULL; +} +//c_str() 2x +Section* Config::GetSection(string const& _sectionname) const{ + for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){ + if (!strcasecmp((*tel)->GetName(),_sectionname.c_str())) return (*tel); + } + return NULL; +} + +Section* Config::GetSectionFromProperty(char const * const prop) const{ + for (const_it tel=sectionlist.begin(); tel!=sectionlist.end(); tel++){ + if ((*tel)->GetPropValue(prop) != NO_SUCH_PROPERTY) return (*tel); + } + return NULL; +} + + +bool Config::ParseConfigFile(char const * const configfilename){ + static bool first_configfile = true; + ifstream in(configfilename); + if (!in) return false; + const char * settings_type = first_configfile?"primary":"additional"; + first_configfile = false; + LOG_MSG("CONFIG:Loading %s settings from config file %s", settings_type,configfilename); + + //Get directory from configfilename, used with relative paths. + current_config_dir=configfilename; + std::string::size_type pos = current_config_dir.rfind(CROSS_FILESPLIT); + if(pos == std::string::npos) pos = 0; //No directory then erase string + current_config_dir.erase(pos); + + string gegevens; + Section* currentsection = NULL; + Section* testsec = NULL; + while (getline(in,gegevens)) { + + /* strip leading/trailing whitespace */ + trim(gegevens); + if(!gegevens.size()) continue; + + switch(gegevens[0]){ + case '%': + case '\0': + case '#': + case ' ': + case '\n': + continue; + break; + case '[': + { + string::size_type loc = gegevens.find(']'); + if(loc == string::npos) continue; + gegevens.erase(loc); + testsec = GetSection(gegevens.substr(1)); + if(testsec != NULL ) currentsection = testsec; + testsec = NULL; + } + break; + default: + try { + if(currentsection) currentsection->HandleInputline(gegevens); + } catch(const char* message) { + message=0; + //EXIT with message + } + break; + } + } + current_config_dir.clear();//So internal changes don't use the path information + return true; +} + +void Config::ParseEnv(char ** envp) { + for(char** env=envp; *env;env++) { + char copy[1024]; + safe_strncpy(copy,*env,1024); + if(strncasecmp(copy,"DOSBOX_",7)) + continue; + char* sec_name = ©[7]; + if(!(*sec_name)) + continue; + char* prop_name = strrchr(sec_name,'_'); + if(!prop_name || !(*prop_name)) + continue; + *prop_name++=0; + Section* sect = GetSection(sec_name); + if(!sect) + continue; + sect->HandleInputline(prop_name); + } +} + +void Config::SetStartUp(void (*_function)(void)) { + _start_function=_function; +} + + +void Config::StartUp(void) { + (*_start_function)(); +} + +bool CommandLine::FindExist(char const * const name,bool remove) { + cmd_it it; + if (!(FindEntry(name,it,false))) return false; + if (remove) cmds.erase(it); + return true; +} + +bool CommandLine::FindHex(char const * const name,int & value,bool remove) { + cmd_it it,it_next; + if (!(FindEntry(name,it,true))) return false; + it_next=it;it_next++; + sscanf((*it_next).c_str(),"%X",&value); + if (remove) cmds.erase(it,++it_next); + return true; +} + +bool CommandLine::FindInt(char const * const name,int & value,bool remove) { + cmd_it it,it_next; + if (!(FindEntry(name,it,true))) return false; + it_next=it;it_next++; + value=atoi((*it_next).c_str()); + if (remove) cmds.erase(it,++it_next); + return true; +} + +bool CommandLine::FindString(char const * const name,std::string & value,bool remove) { + cmd_it it,it_next; + if (!(FindEntry(name,it,true))) return false; + it_next=it;it_next++; + value=*it_next; + if (remove) cmds.erase(it,++it_next); + return true; +} + +bool CommandLine::FindCommand(unsigned int which,std::string & value) { + if (which<1) return false; + if (which>cmds.size()) return false; + cmd_it it=cmds.begin(); + for (;which>1;which--) it++; + value=(*it); + return true; +} + +bool CommandLine::FindEntry(char const * const name,cmd_it & it,bool neednext) { + for (it=cmds.begin();it!=cmds.end();it++) { + if (!strcasecmp((*it).c_str(),name)) { + cmd_it itnext=it;itnext++; + if (neednext && (itnext==cmds.end())) return false; + return true; + } + } + return false; +} + +bool CommandLine::FindStringBegin(char const* const begin,std::string & value, bool remove) { + size_t len = strlen(begin); + for (cmd_it it=cmds.begin();it!=cmds.end();it++) { + if (strncmp(begin,(*it).c_str(),len)==0) { + value=((*it).c_str() + len); + if (remove) cmds.erase(it); + return true; + } + } + return false; +} + +bool CommandLine::FindStringRemain(char const * const name,std::string & value) { + cmd_it it;value=""; + if (!FindEntry(name,it)) return false; + it++; + for (;it!=cmds.end();it++) { + value+=" "; + value+=(*it); + } + return true; +} + +bool CommandLine::GetStringRemain(std::string & value) { + if(!cmds.size()) return false; + + cmd_it it=cmds.begin();value=(*it++); + for(;it != cmds.end();it++) { + value+=" "; + value+=(*it); + } + return true; +} + + +unsigned int CommandLine::GetCount(void) { + return (unsigned int)cmds.size(); +} + +CommandLine::CommandLine(int argc,char const * const argv[]) { + if (argc>0) { + file_name=argv[0]; + } + int i=1; + while (i -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dosbox.h" -#include "debug.h" -#include "support.h" -#include "video.h" - - -void upcase(std::string &str) { - int (*tf)(int) = std::toupper; - std::transform(str.begin(), str.end(), str.begin(), tf); -} - -void lowcase(std::string &str) { - int (*tf)(int) = std::tolower; - std::transform(str.begin(), str.end(), str.begin(), tf); -} - - -/* - Ripped some source from freedos for this one. - -*/ - - -/* - * replaces all instances of character o with character c - */ - - -void strreplace(char * str,char o,char n) { - while (*str) { - if (*str==o) *str=n; - str++; - } -} -char *ltrim(char *str) { - while (*str && isspace(*reinterpret_cast(str))) str++; - return str; -} - -char *rtrim(char *str) { - char *p; - p = strchr(str, '\0'); - while (--p >= str && isspace(*reinterpret_cast(p))) {}; - p[1] = '\0'; - return str; -} - -char *trim(char *str) { - return ltrim(rtrim(str)); -} - -char * upcase(char * str) { - for (char* idx = str; *idx ; idx++) *idx = toupper(*reinterpret_cast(idx)); - return str; -} - -char * lowcase(char * str) { - for(char* idx = str; *idx ; idx++) *idx = tolower(*reinterpret_cast(idx)); - return str; -} - - - -bool ScanCMDBool(char * cmd,char const * const check) { - char * scan=cmd;size_t c_len=strlen(check); - while ((scan=strchr(scan,'/'))) { - /* found a / now see behind it */ - scan++; - if (strncasecmp(scan,check,c_len)==0 && (scan[c_len]==' ' || scan[c_len]=='\t' || scan[c_len]=='/' || scan[c_len]==0)) { - /* Found a math now remove it from the string */ - memmove(scan-1,scan+c_len,strlen(scan+c_len)+1); - trim(scan-1); - return true; - } - } - return false; -} - -/* This scans the command line for a remaining switch and reports it else returns 0*/ -char * ScanCMDRemain(char * cmd) { - char * scan,*found;; - if ((scan=found=strchr(cmd,'/'))) { - while ( *scan && !isspace(*reinterpret_cast(scan)) ) scan++; - *scan=0; - return found; - } else return 0; -} - -char * StripWord(char *&line) { - char * scan=line; - scan=ltrim(scan); - if (*scan=='"') { - char * end_quote=strchr(scan+1,'"'); - if (end_quote) { - *end_quote=0; - line=ltrim(++end_quote); - return (scan+1); - } - } - char * begin=scan; - for (char c = *scan ;(c = *scan);scan++) { - if (isspace(*reinterpret_cast(&c))) { - *scan++=0; - break; - } - } - line=scan; - return begin; -} - -Bits ConvDecWord(char * word) { - bool negative=false;Bitu ret=0; - if (*word=='-') { - negative=true; - word++; - } - while (char c=*word) { - ret*=10; - ret+=c-'0'; - word++; - } - if (negative) return 0-ret; - else return ret; -} - -Bits ConvHexWord(char * word) { - Bitu ret=0; - while (char c=toupper(*reinterpret_cast(word))) { - ret*=16; - if (c>='0' && c<='9') ret+=c-'0'; - else if (c>='A' && c<='F') ret+=10+(c-'A'); - word++; - } - return ret; -} - -double ConvDblWord(char * word) { - return 0.0f; -} - - -static char buf[1024]; //greater scope as else it doesn't always gets thrown right (linux/gcc2.95) -void E_Exit(const char * format,...) { -#if C_DEBUG && C_HEAVY_DEBUG - DEBUG_HeavyWriteLogInstruction(); -#endif - va_list msg; - va_start(msg,format); - vsprintf(buf,format,msg); - va_end(msg); - strcat(buf,"\n"); - - throw(buf); -} +/* + * Copyright (C) 2002-2009 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: support.cpp,v 1.37 2009-05-27 09:15:42 qbix79 Exp $ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dosbox.h" +#include "debug.h" +#include "support.h" +#include "video.h" + + +void upcase(std::string &str) { + int (*tf)(int) = std::toupper; + std::transform(str.begin(), str.end(), str.begin(), tf); +} + +void lowcase(std::string &str) { + int (*tf)(int) = std::tolower; + std::transform(str.begin(), str.end(), str.begin(), tf); +} + + +/* + Ripped some source from freedos for this one. + +*/ + + +/* + * replaces all instances of character o with character c + */ + + +void strreplace(char * str,char o,char n) { + while (*str) { + if (*str==o) *str=n; + str++; + } +} +char *ltrim(char *str) { + while (*str && isspace(*reinterpret_cast(str))) str++; + return str; +} + +char *rtrim(char *str) { + char *p; + p = strchr(str, '\0'); + while (--p >= str && isspace(*reinterpret_cast(p))) {}; + p[1] = '\0'; + return str; +} + +char *trim(char *str) { + return ltrim(rtrim(str)); +} + +char * upcase(char * str) { + for (char* idx = str; *idx ; idx++) *idx = toupper(*reinterpret_cast(idx)); + return str; +} + +char * lowcase(char * str) { + for(char* idx = str; *idx ; idx++) *idx = tolower(*reinterpret_cast(idx)); + return str; +} + + + +bool ScanCMDBool(char * cmd,char const * const check) { + char * scan=cmd;size_t c_len=strlen(check); + while ((scan=strchr(scan,'/'))) { + /* found a / now see behind it */ + scan++; + if (strncasecmp(scan,check,c_len)==0 && (scan[c_len]==' ' || scan[c_len]=='\t' || scan[c_len]=='/' || scan[c_len]==0)) { + /* Found a math now remove it from the string */ + memmove(scan-1,scan+c_len,strlen(scan+c_len)+1); + trim(scan-1); + return true; + } + } + return false; +} + +/* This scans the command line for a remaining switch and reports it else returns 0*/ +char * ScanCMDRemain(char * cmd) { + char * scan,*found;; + if ((scan=found=strchr(cmd,'/'))) { + while ( *scan && !isspace(*reinterpret_cast(scan)) ) scan++; + *scan=0; + return found; + } else return 0; +} + +char * StripWord(char *&line) { + char * scan=line; + scan=ltrim(scan); + if (*scan=='"') { + char * end_quote=strchr(scan+1,'"'); + if (end_quote) { + *end_quote=0; + line=ltrim(++end_quote); + return (scan+1); + } + } + char * begin=scan; + for (char c = *scan ;(c = *scan);scan++) { + if (isspace(*reinterpret_cast(&c))) { + *scan++=0; + break; + } + } + line=scan; + return begin; +} + +Bits ConvDecWord(char * word) { + bool negative=false;Bitu ret=0; + if (*word=='-') { + negative=true; + word++; + } + while (char c=*word) { + ret*=10; + ret+=c-'0'; + word++; + } + if (negative) return 0-ret; + else return ret; +} + +Bits ConvHexWord(char * word) { + Bitu ret=0; + while (char c=toupper(*reinterpret_cast(word))) { + ret*=16; + if (c>='0' && c<='9') ret+=c-'0'; + else if (c>='A' && c<='F') ret+=10+(c-'A'); + word++; + } + return ret; +} + +double ConvDblWord(char * word) { + return 0.0f; +} + + +static char buf[1024]; //greater scope as else it doesn't always gets thrown right (linux/gcc2.95) +void E_Exit(const char * format,...) { +#if C_DEBUG && C_HEAVY_DEBUG + DEBUG_HeavyWriteLogInstruction(); +#endif + va_list msg; + va_start(msg,format); + vsprintf(buf,format,msg); + va_end(msg); + strcat(buf,"\n"); + + throw(buf); +} diff --git a/src/shell/shell.cpp b/src/shell/shell.cpp index 248a44c..fac0aa8 100644 --- a/src/shell/shell.cpp +++ b/src/shell/shell.cpp @@ -1,673 +1,673 @@ -/* - * Copyright (C) 2002-2009 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.cpp,v 1.100 2009/07/08 20:05:41 c2woody Exp $ */ - -#include -#include -#include -#ifdef HW_RVL -#include -#endif -#include "dosbox.h" -#include "regs.h" -#include "control.h" -#include "shell.h" -#include "callback.h" -#include "support.h" - - -Bitu call_shellstop; -/* Larger scope so shell_del autoexec can use it to - * remove things from the environment */ -Program * first_shell = 0; - -static Bitu shellstop_handler(void) { - return CBRET_STOP; -} - -static void SHELL_ProgramStart(Program * * make) { - *make = new DOS_Shell; -} - -#define AUTOEXEC_SIZE 4096 -static char autoexec_data[AUTOEXEC_SIZE] = { 0 }; -static std::list autoexec_strings; -typedef std::list::iterator auto_it; - -void VFILE_Remove(const char *name); - -void AutoexecObject::Install(const std::string &in) { - if(GCC_UNLIKELY(installed)) E_Exit("autoexec: allready created %s",buf.c_str()); - installed = true; - buf = in; - autoexec_strings.push_back(buf); - this->CreateAutoexec(); - - //autoexec.bat is normally created AUTOEXEC_Init. - //But if we are allready running (first_shell) - //we have to update the envirionment to display changes - - if(first_shell) { - //create a copy as the string will be modified - std::string::size_type n = buf.size(); - char* buf2 = new char[n + 1]; - safe_strncpy(buf2, buf.c_str(), n + 1); - if((strncasecmp(buf2,"set ",4) == 0) && (strlen(buf2) > 4)){ - char* after_set = buf2 + 4;//move to variable that is being set - char* test = strpbrk(after_set,"="); - if(!test) {first_shell->SetEnv(after_set,"");return;} - *test++ = 0; - //If the shell is running/exists update the environment - first_shell->SetEnv(after_set,test); - } - delete [] buf2; - } -} - -void AutoexecObject::InstallBefore(const std::string &in) { - if(GCC_UNLIKELY(installed)) E_Exit("autoexec: allready created %s",buf.c_str()); - installed = true; - buf = in; - autoexec_strings.push_front(buf); - this->CreateAutoexec(); -} - -void AutoexecObject::CreateAutoexec(void) { - /* Remove old autoexec.bat if the shell exists */ - if(first_shell) VFILE_Remove("AUTOEXEC.BAT"); - - //Create a new autoexec.bat - autoexec_data[0] = 0; - size_t auto_len; - for(auto_it it= autoexec_strings.begin(); it != autoexec_strings.end(); it++) { - auto_len = strlen(autoexec_data); - if ((auto_len+(*it).length()+3)>AUTOEXEC_SIZE) { - E_Exit("SYSTEM:Autoexec.bat file overflow"); - } - sprintf((autoexec_data+auto_len),"%s\r\n",(*it).c_str()); - } - if(first_shell) VFILE_Register("AUTOEXEC.BAT",(Bit8u *)autoexec_data,(Bit32u)strlen(autoexec_data)); -} - -AutoexecObject::~AutoexecObject(){ - if(!installed) return; - - // Remove the line from the autoexecbuffer and update environment - for(auto_it it = autoexec_strings.begin(); it != autoexec_strings.end(); ) { - if((*it) == buf) { - it = autoexec_strings.erase(it); - std::string::size_type n = buf.size(); - char* buf2 = new char[n + 1]; - safe_strncpy(buf2, buf.c_str(), n + 1); - // If it's a environment variable remove it from there as well - if((strncasecmp(buf2,"set ",4) == 0) && (strlen(buf2) > 4)){ - char* after_set = buf2 + 4;//move to variable that is being set - char* test = strpbrk(after_set,"="); - if(!test) continue; - *test = 0; - //If the shell is running/exists update the environment - if(first_shell) first_shell->SetEnv(after_set,""); - } - delete [] buf2; - } else it++; - } - this->CreateAutoexec(); -} - -DOS_Shell::DOS_Shell():Program(){ - input_handle=STDIN; - echo=true; - exit=false; - bf=0; - call=false; - completion_start = NULL; -} - -Bitu DOS_Shell::GetRedirection(char *s, char **ifn, char **ofn,bool * append) { - - char * lr=s; - char * lw=s; - char ch; - Bitu num=0; - bool quote = false; - - while ( (ch=*lr++) ) { - if(quote && ch != '"') { /* don't parse redirection within quotes. Not perfect yet. Escaped quotes will mess the count up */ - *lw++ = ch; - continue; - } - - switch (ch) { - case '"': - quote = !quote; - break; - case '>': - *append=((*lr)=='>'); - if (*append) lr++; - lr=ltrim(lr); - if (*ofn) free(*ofn); - *ofn=lr; - while (*lr && *lr!=' ') lr++; - //if it ends on a : => remove it. - if((*ofn != lr) && (lr[-1] == ':')) lr[-1] = 0; - if(*lr && *(lr+1)) - *lr++=0; - else - *lr=0; - *ofn=strdup(*ofn); - continue; - case '<': - if (*ifn) free(*ifn); - lr=ltrim(lr); - *ifn=lr; - while (*lr && *lr!=' ') lr++; - if((*ifn != lr) && (lr[-1] == ':')) lr[-1] = 0; - if(*lr && *(lr+1)) - *lr++=0; - else - *lr=0; - *ifn=strdup(*ifn); - continue; - case '|': - ch=0; - num++; - } - *lw++=ch; - } - *lw=0; - return num; -} - -void DOS_Shell::ParseLine(char * line) { - LOG(LOG_EXEC,LOG_ERROR)("Parsing command line: %s",line); - /* Check for a leading @ */ - if (line[0] == '@') line[0] = ' '; - line = trim(line); - - /* Do redirection and pipe checks */ - - char * in = 0; - char * out = 0; - - Bit16u dummy,dummy2; - Bit32u bigdummy = 0; - Bitu num = 0; /* Number of commands in this line */ - bool append; - bool normalstdin = false; /* wether stdin/out are open on start. */ - bool normalstdout = false; /* Bug: Assumed is they are "con" */ - - num = GetRedirection(line,&in, &out,&append); - if (num>1) LOG_MSG("SHELL:Multiple command on 1 line not supported"); - if (in || out) { - normalstdin = (psp->GetFileHandle(0) != 0xff); - normalstdout = (psp->GetFileHandle(1) != 0xff); - } - if (in) { - if(DOS_OpenFile(in,OPEN_READ,&dummy)) { //Test if file exists - DOS_CloseFile(dummy); - LOG_MSG("SHELL:Redirect input from %s",in); - if(normalstdin) DOS_CloseFile(0); //Close stdin - DOS_OpenFile(in,OPEN_READ,&dummy); //Open new stdin - } - } - if (out){ - LOG_MSG("SHELL:Redirect output to %s",out); - if(normalstdout) DOS_CloseFile(1); - if(!normalstdin && !in) DOS_OpenFile("con",OPEN_READWRITE,&dummy); - bool status = true; - /* Create if not exist. Open if exist. Both in read/write mode */ - if(append) { - if( (status = DOS_OpenFile(out,OPEN_READWRITE,&dummy)) ) { - DOS_SeekFile(1,&bigdummy,DOS_SEEK_END); - } else { - status = DOS_CreateFile(out,DOS_ATTR_ARCHIVE,&dummy); //Create if not exists. - } - } else { - status = DOS_OpenFileExtended(out,OPEN_READWRITE,DOS_ATTR_ARCHIVE,0x12,&dummy,&dummy2); - } - - if(!status && normalstdout) DOS_OpenFile("con",OPEN_READWRITE,&dummy); //Read only file, open con again - if(!normalstdin && !in) DOS_CloseFile(0); - } - /* Run the actual command */ - DoCommand(line); - /* Restore handles */ - if(in) { - DOS_CloseFile(0); - if(normalstdin) DOS_OpenFile("con",OPEN_READWRITE,&dummy); - free(in); - } - if(out) { - DOS_CloseFile(1); - if(!normalstdin) DOS_OpenFile("con",OPEN_READWRITE,&dummy); - if(normalstdout) DOS_OpenFile("con",OPEN_READWRITE,&dummy); - if(!normalstdin) DOS_CloseFile(0); - free(out); - } -} - - - -void DOS_Shell::RunInternal(void) -{ - char input_line[CMD_MAXLINE] = {0}; - while(bf && bf->ReadLine(input_line)) - { - if (echo) { - if (input_line[0] != '@') { - ShowPrompt(); - WriteOut_NoParsing(input_line); - WriteOut_NoParsing("\n"); - }; - }; - ParseLine(input_line); - } - return; -} - -void DOS_Shell::Run(void) { - char input_line[CMD_MAXLINE] = {0}; - std::string line; - if (cmd->FindStringRemain("/C",line)) { - strcpy(input_line,line.c_str()); - DOS_Shell temp; - temp.echo = echo; - temp.ParseLine(input_line); //for *.exe *.com |*.bat creates the bf needed by runinternal; - temp.RunInternal(); // exits when no bf is found. - return; - } - /* Start a normal shell and check for a first command init */ - WriteOut(MSG_Get("SHELL_STARTUP_BEGIN"),VERSION); -#if C_DEBUG - WriteOut(MSG_Get("SHELL_STARTUP_DEBUG")); -#endif - if (machine == MCH_CGA) WriteOut(MSG_Get("SHELL_STARTUP_CGA")); - WriteOut(MSG_Get("SHELL_STARTUP_END")); - - if (cmd->FindString("/INIT",line,true)) { - strcpy(input_line,line.c_str()); - line.erase(); - ParseLine(input_line); - } - do { - if (bf){ - if(bf->ReadLine(input_line)) { - if (echo) { - if (input_line[0]!='@') { - ShowPrompt(); - WriteOut_NoParsing(input_line); - WriteOut_NoParsing("\n"); - }; - }; - ParseLine(input_line); - if (echo) WriteOut("\n"); - } - } else { - if (echo) ShowPrompt(); - InputCommand(input_line); - ParseLine(input_line); - if (echo && !bf) WriteOut_NoParsing("\n"); - } - } while (!exit); -} - -void DOS_Shell::SyntaxError(void) { - WriteOut(MSG_Get("SHELL_SYNTAXERROR")); -} - -class AUTOEXEC:public Module_base { -private: - AutoexecObject autoexec[17]; - AutoexecObject autoexec_echo; -public: - AUTOEXEC(Section* configuration):Module_base(configuration) { - /* Register a virtual AUOEXEC.BAT file */ - std::string line; - Section_line * section=static_cast(configuration); - - /* Check -securemode switch to disable mount/imgmount/boot after running autoexec.bat */ - bool secure = control->cmdline->FindExist("-securemode",true); - - /* add stuff from the configfile unless -noautexec or -securemode is specified. */ - char * extra = const_cast(section->data.c_str()); - if (extra && !secure && !control->cmdline->FindExist("-noautoexec",true)) { - /* detect if "echo off" is the first line */ - bool echo_off = !strncasecmp(extra,"echo off",8); - if (!echo_off) echo_off = !strncasecmp(extra,"@echo off",9); - - /* if "echo off" add it to the front of autoexec.bat */ - if(echo_off) autoexec_echo.InstallBefore("@echo off"); - - /* Install the stuff from the configfile */ - autoexec[0].Install(section->data); - } - - /* Check to see for extra command line options to be added (before the command specified on commandline) */ - /* Maximum of extra commands: 10 */ - Bitu i = 1; - while (control->cmdline->FindString("-c",line,true) && (i <= 11)) { -#if defined (WIN32) || defined (OS2) - //replace single with double quotes so that mount commands can contain spaces - for(Bitu temp = 0;temp < line.size();++temp) if(line[temp] == '\'') line[temp]='\"'; -#endif //Linux users can simply use \" in their shell - autoexec[i++].Install(line); - } - - /* Check for the -exit switch which causes dosbox to when the command on the commandline has finished */ - bool addexit = control->cmdline->FindExist("-exit",true); - - /* Check for first command being a directory or file */ - char buffer[CROSS_LEN]; - char cross_filesplit[2] = {CROSS_FILESPLIT , 0}; - /* Combining -securemode and no parameter leaves you with a lovely Z:\. */ - if ( !control->cmdline->FindCommand(1,line) ) { - if ( secure ) autoexec[12].Install("z:\\config.com -securemode"); - } else { - struct stat test; - strcpy(buffer,line.c_str()); - if (stat(buffer,&test)){ - getcwd(buffer,CROSS_LEN); - strcat(buffer,cross_filesplit); - strcat(buffer,line.c_str()); - if (stat(buffer,&test)) goto nomount; - } - if (test.st_mode & S_IFDIR) { - autoexec[12].Install(std::string("MOUNT C \"") + buffer + "\""); - autoexec[13].Install("C:"); - if(secure) autoexec[14].Install("z:\\config.com -securemode"); - } else { - char* name = strrchr(buffer,CROSS_FILESPLIT); - if (!name) { //Only a filename - line = buffer; - getcwd(buffer,CROSS_LEN); - strcat(buffer,cross_filesplit); - strcat(buffer,line.c_str()); - if(stat(buffer,&test)) goto nomount; - name = strrchr(buffer,CROSS_FILESPLIT); - if(!name) goto nomount; - } - *name++ = 0; - if (access(buffer,F_OK)) goto nomount; - autoexec[12].Install(std::string("MOUNT C \"") + buffer + "\""); - autoexec[13].Install("C:"); - upcase(name); - if(strstr(name,".BAT") != 0) { - if(secure) autoexec[14].Install("z:\\config.com -securemode"); - /* BATch files are called else exit will not work */ - autoexec[15].Install(std::string("CALL ") + name); - } else if((strstr(name,".IMG") != 0) || (strstr(name,".IMA") !=0)) { - //No secure mode here as boot is destructive and enabling securemode disables boot - /* Boot image files */ - autoexec[15].Install(std::string("BOOT ") + name); - } else { - if(secure) autoexec[14].Install("z:\\config.com -securemode"); - autoexec[15].Install(name); - } - - if(addexit) autoexec[16].Install("exit"); - } - } -nomount: - VFILE_Register("AUTOEXEC.BAT",(Bit8u *)autoexec_data,(Bit32u)strlen(autoexec_data)); - } -}; - -static AUTOEXEC* test; - -void AUTOEXEC_Init(Section * sec) { - test = new AUTOEXEC(sec); -} - -static char const * const path_string="PATH=Z:\\"; -static char const * const comspec_string="COMSPEC=Z:\\COMMAND.COM"; -static char const * const full_name="Z:\\COMMAND.COM"; -static char const * const init_line="/INIT AUTOEXEC.BAT"; - -void SHELL_Init() { - /* Add messages */ - MSG_Add("SHELL_ILLEGAL_PATH","Illegal Path.\n"); - MSG_Add("SHELL_CMD_HELP","If you want a list of all supported commands type \033[33;1mhelp /all\033[0m .\nA short list of the most often used commands:\n"); - MSG_Add("SHELL_CMD_ECHO_ON","ECHO is on.\n"); - MSG_Add("SHELL_CMD_ECHO_OFF","ECHO is off.\n"); - MSG_Add("SHELL_ILLEGAL_SWITCH","Illegal switch: %s.\n"); - MSG_Add("SHELL_MISSING_PARAMETER","Required parameter missing.\n"); - MSG_Add("SHELL_CMD_CHDIR_ERROR","Unable to change to: %s.\n"); - MSG_Add("SHELL_CMD_CHDIR_HINT","To change to different drive type \033[31m%c:\033[0m\n"); - MSG_Add("SHELL_CMD_CHDIR_HINT_2","directoryname is longer than 8 charachters and/or contains spaces.\nTry \033[31mcd %s\033[0m\n"); - MSG_Add("SHELL_CMD_CHDIR_HINT_3","You are still on drive Z:, change to a mounted drive with \033[31mC:\033[0m.\n"); - MSG_Add("SHELL_CMD_MKDIR_ERROR","Unable to make: %s.\n"); - MSG_Add("SHELL_CMD_RMDIR_ERROR","Unable to remove: %s.\n"); - MSG_Add("SHELL_CMD_DEL_ERROR","Unable to delete: %s.\n"); - MSG_Add("SHELL_SYNTAXERROR","The syntax of the command is incorrect.\n"); - MSG_Add("SHELL_CMD_SET_NOT_SET","Environment variable %s not defined.\n"); - MSG_Add("SHELL_CMD_SET_OUT_OF_SPACE","Not enough environment space left.\n"); - MSG_Add("SHELL_CMD_IF_EXIST_MISSING_FILENAME","IF EXIST: Missing filename.\n"); - MSG_Add("SHELL_CMD_IF_ERRORLEVEL_MISSING_NUMBER","IF ERRORLEVEL: Missing number.\n"); - MSG_Add("SHELL_CMD_IF_ERRORLEVEL_INVALID_NUMBER","IF ERRORLEVEL: Invalid number.\n"); - MSG_Add("SHELL_CMD_GOTO_MISSING_LABEL","No label supplied to GOTO command.\n"); - MSG_Add("SHELL_CMD_GOTO_LABEL_NOT_FOUND","GOTO: Label %s not found.\n"); - MSG_Add("SHELL_CMD_FILE_NOT_FOUND","File %s not found.\n"); - MSG_Add("SHELL_CMD_FILE_EXISTS","File %s already exists.\n"); - MSG_Add("SHELL_CMD_DIR_INTRO","Directory of %s.\n"); - MSG_Add("SHELL_CMD_DIR_BYTES_USED","%5d File(s) %17s Bytes.\n"); - MSG_Add("SHELL_CMD_DIR_BYTES_FREE","%5d Dir(s) %17s Bytes free.\n"); - MSG_Add("SHELL_EXECUTE_DRIVE_NOT_FOUND","Drive %c does not exist!\nYou must \033[31mmount\033[0m it first. Type \033[1;33mintro\033[0m or \033[1;33mintro mount\033[0m for more information.\n"); - MSG_Add("SHELL_EXECUTE_ILLEGAL_COMMAND","Illegal command: %s.\n"); - MSG_Add("SHELL_CMD_PAUSE","Press any key to continue.\n"); - MSG_Add("SHELL_CMD_PAUSE_HELP","Waits for 1 keystroke to continue.\n"); - MSG_Add("SHELL_CMD_COPY_FAILURE","Copy failure : %s.\n"); - MSG_Add("SHELL_CMD_COPY_SUCCESS"," %d File(s) copied.\n"); - MSG_Add("SHELL_CMD_SUBST_NO_REMOVE","Removing drive not supported. Doing nothing.\n"); - MSG_Add("SHELL_CMD_SUBST_FAILURE","SUBST failed. You either made an error in your commandline or the target drive is already used.\nIt's only possible to use SUBST on Local drives"); - -#ifdef HW_RVL - MSG_Add("SHELL_STARTUP_BEGIN", - "\033[44;1m========================" - "========================" - "======================\n" - "| \033[32mWelcome to DOSBox v%-8s\033[37m |\n" - "| |\n" -// "| DOSBox runs real and protected mode games. |\n" - "| For a short introduction for new users type: \033[33mINTRO\033[37m |\n" - "| For supported shell commands type: \033[33mHELP\033[37m |\n" - "| |\n" - "| If you want more speed, try \033[31mctrl-F8\033[37m and \033[31mctrl-F12\033[37m. |\n" - "| To activate the keymapper \033[31mctrl-F1\033[37m. |\n" - "| For more information read the \033[36mREADME\033[37m file in the DOSBox directory. |\n" - "| |\n" - ); - MSG_Add("SHELL_STARTUP_CGA","| DOSBox supports Composite CGA mode. |\n" - "| Use \033[31m(alt-)F11\033[37m to change the colours when in this mode. |\n" - "| |\n" - ); - MSG_Add("SHELL_STARTUP_DEBUG", - "| Press \033[31malt-Pause\033[37m to enter the debugger or start the exe with \033[33mDEBUG\033[37m. |\n" - "| |\n" - ); - MSG_Add("SHELL_STARTUP_END", - "| \033[32mHAVE FUN!\033[37m |\n" - "| \033[32mThe DOSBox Team\033[37m |\n" - "========================" - "========================" - "======================\033[0m\n" - //"\n" //Breaks the startup message if you type a mount and a drive change. - ); -#else - MSG_Add("SHELL_STARTUP_BEGIN", - "\033[44;1m\xC9\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" - "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" - "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBB\n" - "\xBA \033[32mWelcome to DOSBox v%-8s\033[37m \xBA\n" - "\xBA \xBA\n" -// "\xBA DOSBox runs real and protected mode games. \xBA\n" - "\xBA For a short introduction for new users type: \033[33mINTRO\033[37m \xBA\n" - "\xBA For supported shell commands type: \033[33mHELP\033[37m \xBA\n" - "\xBA \xBA\n" - "\xBA If you want more speed, try \033[31mctrl-F8\033[37m and \033[31mctrl-F12\033[37m. \xBA\n" - "\xBA To activate the keymapper \033[31mctrl-F1\033[37m. \xBA\n" - "\xBA For more information read the \033[36mREADME\033[37m file in the DOSBox directory. \xBA\n" - "\xBA \xBA\n" - ); - MSG_Add("SHELL_STARTUP_CGA","\xBA DOSBox supports Composite CGA mode. \xBA\n" - "\xBA Use \033[31m(alt-)F11\033[37m to change the colours when in this mode. \xBA\n" - "\xBA \xBA\n" - ); - MSG_Add("SHELL_STARTUP_DEBUG", - "\xBA Press \033[31malt-Pause\033[37m to enter the debugger or start the exe with \033[33mDEBUG\033[37m. \xBA\n" - "\xBA \xBA\n" - ); - MSG_Add("SHELL_STARTUP_END", - "\xBA \033[32mHAVE FUN!\033[37m \xBA\n" - "\xBA \033[32mThe DOSBox Team \033[33mhttp://www.dosbox.com\033[37m \xBA\n" - "\xC8\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" - "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" - "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBC\033[0m\n" - //"\n" //Breaks the startup message if you type a mount and a drive change. - ); -#endif - MSG_Add("SHELL_CMD_CHDIR_HELP","Displays/changes the current directory.\n"); - MSG_Add("SHELL_CMD_CHDIR_HELP_LONG","CHDIR [drive:][path]\n" - "CHDIR [..]\n" - "CD [drive:][path]\n" - "CD [..]\n\n" - " .. Specifies that you want to change to the parent directory.\n\n" - "Type CD drive: to display the current directory in the specified drive.\n" - "Type CD without parameters to display the current drive and directory.\n"); - MSG_Add("SHELL_CMD_CLS_HELP","Clear screen.\n"); - MSG_Add("SHELL_CMD_DIR_HELP","Directory View.\n"); - MSG_Add("SHELL_CMD_ECHO_HELP","Display messages and enable/disable command echoing.\n"); - MSG_Add("SHELL_CMD_EXIT_HELP","Exit from the shell.\n"); - MSG_Add("SHELL_CMD_HELP_HELP","Show help.\n"); - MSG_Add("SHELL_CMD_MKDIR_HELP","Make Directory.\n"); - MSG_Add("SHELL_CMD_MKDIR_HELP_LONG","MKDIR [drive:][path]\n" - "MD [drive:][path]\n"); - MSG_Add("SHELL_CMD_RMDIR_HELP","Remove Directory.\n"); - MSG_Add("SHELL_CMD_RMDIR_HELP_LONG","RMDIR [drive:][path]\n" - "RD [drive:][path]\n"); - MSG_Add("SHELL_CMD_SET_HELP","Change environment variables.\n"); - MSG_Add("SHELL_CMD_IF_HELP","Performs conditional processing in batch programs.\n"); - MSG_Add("SHELL_CMD_GOTO_HELP","Jump to a labeled line in a batch script.\n"); - MSG_Add("SHELL_CMD_SHIFT_HELP","Leftshift commandline parameters in a batch script.\n"); - MSG_Add("SHELL_CMD_TYPE_HELP","Display the contents of a text-file.\n"); - MSG_Add("SHELL_CMD_TYPE_HELP_LONG","TYPE [drive:][path][filename]\n"); - MSG_Add("SHELL_CMD_REM_HELP","Add comments in a batch file.\n"); - MSG_Add("SHELL_CMD_REM_HELP_LONG","REM [comment]\n"); - MSG_Add("SHELL_CMD_NO_WILD","This is a simple version of the command, no wildcards allowed!\n"); - MSG_Add("SHELL_CMD_RENAME_HELP","Renames one or more files.\n"); - MSG_Add("SHELL_CMD_RENAME_HELP_LONG","RENAME [drive:][path]filename1 filename2.\n" - "REN [drive:][path]filename1 filename2.\n\n" - "Note that you can not specify a new drive or path for your destination file.\n"); - MSG_Add("SHELL_CMD_DELETE_HELP","Removes one or more files.\n"); - MSG_Add("SHELL_CMD_COPY_HELP","Copy files.\n"); - MSG_Add("SHELL_CMD_CALL_HELP","Start a batch file from within another batch file.\n"); - MSG_Add("SHELL_CMD_SUBST_HELP","Assign an internal directory to a drive.\n"); - MSG_Add("SHELL_CMD_LOADHIGH_HELP","Loads a program into upper memory (requires xms=true,umb=true).\n"); - MSG_Add("SHELL_CMD_CHOICE_HELP","Waits for a keypress and sets ERRORLEVEL.\n"); - MSG_Add("SHELL_CMD_CHOICE_HELP_LONG","CHOICE [/C:choices] [/N] [/S] text\n" - " /C[:]choices - Specifies allowable keys. Default is: yn.\n" - " /N - Do not display the choices at end of prompt.\n" - " /S - Enables case-sensitive choices to be selected.\n" - " text - The text to display as a prompt.\n"); - MSG_Add("SHELL_CMD_ATTRIB_HELP","Does nothing. Provided for compatibility.\n"); - MSG_Add("SHELL_CMD_PATH_HELP","Provided for compatibility.\n"); - MSG_Add("SHELL_CMD_VER_HELP","View and set the reported DOS version.\n"); - MSG_Add("SHELL_CMD_VER_VER","DOSBox version %s. Reported DOS version %d.%02d.\n"); - - /* Regular startup */ - call_shellstop=CALLBACK_Allocate(); - /* Setup the startup CS:IP to kill the last running machine when exitted */ - RealPt newcsip=CALLBACK_RealPointer(call_shellstop); - SegSet16(cs,RealSeg(newcsip)); - reg_ip=RealOff(newcsip); - - CALLBACK_Setup(call_shellstop,shellstop_handler,CB_IRET,"shell stop"); - PROGRAMS_MakeFile("COMMAND.COM",SHELL_ProgramStart); - - /* Now call up the shell for the first time */ - Bit16u psp_seg=DOS_FIRST_SHELL; - Bit16u env_seg=DOS_FIRST_SHELL+19; //DOS_GetMemory(1+(4096/16))+1; - Bit16u stack_seg=DOS_GetMemory(2048/16); - SegSet16(ss,stack_seg); - reg_sp=2046; - - /* Set up int 24 and psp (Telarium games) */ - real_writeb(psp_seg+16+1,0,0xea); /* far jmp */ - real_writed(psp_seg+16+1,1,real_readd(0,0x24*4)); - real_writed(0,0x24*4,((Bit32u)psp_seg<<16) | ((16+1)<<4)); - - /* Set up int 23 to "int 20" in the psp. Fixes what.exe */ - real_writed(0,0x23*4,((Bit32u)psp_seg<<16)); - - /* Setup MCBs */ - DOS_MCB pspmcb((Bit16u)(psp_seg-1)); - pspmcb.SetPSPSeg(psp_seg); // MCB of the command shell psp - pspmcb.SetSize(0x10+2); - pspmcb.SetType(0x4d); - DOS_MCB envmcb((Bit16u)(env_seg-1)); - envmcb.SetPSPSeg(psp_seg); // MCB of the command shell environment - envmcb.SetSize(DOS_MEM_START-env_seg); - envmcb.SetType(0x4d); - - /* Setup environment */ - PhysPt env_write=PhysMake(env_seg,0); - MEM_BlockWrite(env_write,path_string,(Bitu)(strlen(path_string)+1)); - env_write += (PhysPt)(strlen(path_string)+1); - MEM_BlockWrite(env_write,comspec_string,(Bitu)(strlen(comspec_string)+1)); - env_write += (PhysPt)(strlen(comspec_string)+1); - mem_writeb(env_write++,0); - mem_writew(env_write,1); - env_write+=2; - MEM_BlockWrite(env_write,full_name,(Bitu)(strlen(full_name)+1)); - - DOS_PSP psp(psp_seg); - psp.MakeNew(0); - dos.psp(psp_seg); - - /* The start of the filetable in the psp must look like this: - * 01 01 01 00 02 - * In order to achieve this: First open 2 files. Close the first and - * duplicate the second (so the entries get 01) */ - Bit16u dummy=0; - DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDIN */ - DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDOUT */ - DOS_CloseFile(0); /* Close STDIN */ - DOS_ForceDuplicateEntry(1,0); /* "new" STDIN */ - DOS_ForceDuplicateEntry(1,2); /* STDERR */ - DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDAUX */ - DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDPRN */ - - psp.SetParent(psp_seg); - /* Set the environment */ - psp.SetEnvironment(env_seg); - /* Set the command line for the shell start up */ - CommandTail tail; - tail.count=(Bit8u)strlen(init_line); - strcpy(tail.buffer,init_line); - MEM_BlockWrite(PhysMake(psp_seg,128),&tail,128); - - /* Setup internal DOS Variables */ - dos.dta(RealMake(psp_seg,0x80)); - dos.psp(psp_seg); - - - SHELL_ProgramStart(&first_shell); - first_shell->Run(); - delete first_shell; - first_shell = 0;//Make clear that it shouldn't be used anymore -} +/* + * Copyright (C) 2002-2009 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.cpp,v 1.100 2009-07-08 20:05:41 c2woody Exp $ */ + +#include +#include +#include +#ifdef HW_RVL +#include +#endif +#include "dosbox.h" +#include "regs.h" +#include "control.h" +#include "shell.h" +#include "callback.h" +#include "support.h" + + +Bitu call_shellstop; +/* Larger scope so shell_del autoexec can use it to + * remove things from the environment */ +Program * first_shell = 0; + +static Bitu shellstop_handler(void) { + return CBRET_STOP; +} + +static void SHELL_ProgramStart(Program * * make) { + *make = new DOS_Shell; +} + +#define AUTOEXEC_SIZE 4096 +static char autoexec_data[AUTOEXEC_SIZE] = { 0 }; +static std::list autoexec_strings; +typedef std::list::iterator auto_it; + +void VFILE_Remove(const char *name); + +void AutoexecObject::Install(const std::string &in) { + if(GCC_UNLIKELY(installed)) E_Exit("autoexec: allready created %s",buf.c_str()); + installed = true; + buf = in; + autoexec_strings.push_back(buf); + this->CreateAutoexec(); + + //autoexec.bat is normally created AUTOEXEC_Init. + //But if we are allready running (first_shell) + //we have to update the envirionment to display changes + + if(first_shell) { + //create a copy as the string will be modified + std::string::size_type n = buf.size(); + char* buf2 = new char[n + 1]; + safe_strncpy(buf2, buf.c_str(), n + 1); + if((strncasecmp(buf2,"set ",4) == 0) && (strlen(buf2) > 4)){ + char* after_set = buf2 + 4;//move to variable that is being set + char* test = strpbrk(after_set,"="); + if(!test) {first_shell->SetEnv(after_set,"");return;} + *test++ = 0; + //If the shell is running/exists update the environment + first_shell->SetEnv(after_set,test); + } + delete [] buf2; + } +} + +void AutoexecObject::InstallBefore(const std::string &in) { + if(GCC_UNLIKELY(installed)) E_Exit("autoexec: allready created %s",buf.c_str()); + installed = true; + buf = in; + autoexec_strings.push_front(buf); + this->CreateAutoexec(); +} + +void AutoexecObject::CreateAutoexec(void) { + /* Remove old autoexec.bat if the shell exists */ + if(first_shell) VFILE_Remove("AUTOEXEC.BAT"); + + //Create a new autoexec.bat + autoexec_data[0] = 0; + size_t auto_len; + for(auto_it it= autoexec_strings.begin(); it != autoexec_strings.end(); it++) { + auto_len = strlen(autoexec_data); + if ((auto_len+(*it).length()+3)>AUTOEXEC_SIZE) { + E_Exit("SYSTEM:Autoexec.bat file overflow"); + } + sprintf((autoexec_data+auto_len),"%s\r\n",(*it).c_str()); + } + if(first_shell) VFILE_Register("AUTOEXEC.BAT",(Bit8u *)autoexec_data,(Bit32u)strlen(autoexec_data)); +} + +AutoexecObject::~AutoexecObject(){ + if(!installed) return; + + // Remove the line from the autoexecbuffer and update environment + for(auto_it it = autoexec_strings.begin(); it != autoexec_strings.end(); ) { + if((*it) == buf) { + it = autoexec_strings.erase(it); + std::string::size_type n = buf.size(); + char* buf2 = new char[n + 1]; + safe_strncpy(buf2, buf.c_str(), n + 1); + // If it's a environment variable remove it from there as well + if((strncasecmp(buf2,"set ",4) == 0) && (strlen(buf2) > 4)){ + char* after_set = buf2 + 4;//move to variable that is being set + char* test = strpbrk(after_set,"="); + if(!test) continue; + *test = 0; + //If the shell is running/exists update the environment + if(first_shell) first_shell->SetEnv(after_set,""); + } + delete [] buf2; + } else it++; + } + this->CreateAutoexec(); +} + +DOS_Shell::DOS_Shell():Program(){ + input_handle=STDIN; + echo=true; + exit=false; + bf=0; + call=false; + completion_start = NULL; +} + +Bitu DOS_Shell::GetRedirection(char *s, char **ifn, char **ofn,bool * append) { + + char * lr=s; + char * lw=s; + char ch; + Bitu num=0; + bool quote = false; + + while ( (ch=*lr++) ) { + if(quote && ch != '"') { /* don't parse redirection within quotes. Not perfect yet. Escaped quotes will mess the count up */ + *lw++ = ch; + continue; + } + + switch (ch) { + case '"': + quote = !quote; + break; + case '>': + *append=((*lr)=='>'); + if (*append) lr++; + lr=ltrim(lr); + if (*ofn) free(*ofn); + *ofn=lr; + while (*lr && *lr!=' ') lr++; + //if it ends on a : => remove it. + if((*ofn != lr) && (lr[-1] == ':')) lr[-1] = 0; + if(*lr && *(lr+1)) + *lr++=0; + else + *lr=0; + *ofn=strdup(*ofn); + continue; + case '<': + if (*ifn) free(*ifn); + lr=ltrim(lr); + *ifn=lr; + while (*lr && *lr!=' ') lr++; + if((*ifn != lr) && (lr[-1] == ':')) lr[-1] = 0; + if(*lr && *(lr+1)) + *lr++=0; + else + *lr=0; + *ifn=strdup(*ifn); + continue; + case '|': + ch=0; + num++; + } + *lw++=ch; + } + *lw=0; + return num; +} + +void DOS_Shell::ParseLine(char * line) { + LOG(LOG_EXEC,LOG_ERROR)("Parsing command line: %s",line); + /* Check for a leading @ */ + if (line[0] == '@') line[0] = ' '; + line = trim(line); + + /* Do redirection and pipe checks */ + + char * in = 0; + char * out = 0; + + Bit16u dummy,dummy2; + Bit32u bigdummy = 0; + Bitu num = 0; /* Number of commands in this line */ + bool append; + bool normalstdin = false; /* wether stdin/out are open on start. */ + bool normalstdout = false; /* Bug: Assumed is they are "con" */ + + num = GetRedirection(line,&in, &out,&append); + if (num>1) LOG_MSG("SHELL:Multiple command on 1 line not supported"); + if (in || out) { + normalstdin = (psp->GetFileHandle(0) != 0xff); + normalstdout = (psp->GetFileHandle(1) != 0xff); + } + if (in) { + if(DOS_OpenFile(in,OPEN_READ,&dummy)) { //Test if file exists + DOS_CloseFile(dummy); + LOG_MSG("SHELL:Redirect input from %s",in); + if(normalstdin) DOS_CloseFile(0); //Close stdin + DOS_OpenFile(in,OPEN_READ,&dummy); //Open new stdin + } + } + if (out){ + LOG_MSG("SHELL:Redirect output to %s",out); + if(normalstdout) DOS_CloseFile(1); + if(!normalstdin && !in) DOS_OpenFile("con",OPEN_READWRITE,&dummy); + bool status = true; + /* Create if not exist. Open if exist. Both in read/write mode */ + if(append) { + if( (status = DOS_OpenFile(out,OPEN_READWRITE,&dummy)) ) { + DOS_SeekFile(1,&bigdummy,DOS_SEEK_END); + } else { + status = DOS_CreateFile(out,DOS_ATTR_ARCHIVE,&dummy); //Create if not exists. + } + } else { + status = DOS_OpenFileExtended(out,OPEN_READWRITE,DOS_ATTR_ARCHIVE,0x12,&dummy,&dummy2); + } + + if(!status && normalstdout) DOS_OpenFile("con",OPEN_READWRITE,&dummy); //Read only file, open con again + if(!normalstdin && !in) DOS_CloseFile(0); + } + /* Run the actual command */ + DoCommand(line); + /* Restore handles */ + if(in) { + DOS_CloseFile(0); + if(normalstdin) DOS_OpenFile("con",OPEN_READWRITE,&dummy); + free(in); + } + if(out) { + DOS_CloseFile(1); + if(!normalstdin) DOS_OpenFile("con",OPEN_READWRITE,&dummy); + if(normalstdout) DOS_OpenFile("con",OPEN_READWRITE,&dummy); + if(!normalstdin) DOS_CloseFile(0); + free(out); + } +} + + + +void DOS_Shell::RunInternal(void) +{ + char input_line[CMD_MAXLINE] = {0}; + while(bf && bf->ReadLine(input_line)) + { + if (echo) { + if (input_line[0] != '@') { + ShowPrompt(); + WriteOut_NoParsing(input_line); + WriteOut_NoParsing("\n"); + }; + }; + ParseLine(input_line); + } + return; +} + +void DOS_Shell::Run(void) { + char input_line[CMD_MAXLINE] = {0}; + std::string line; + if (cmd->FindStringRemain("/C",line)) { + strcpy(input_line,line.c_str()); + DOS_Shell temp; + temp.echo = echo; + temp.ParseLine(input_line); //for *.exe *.com |*.bat creates the bf needed by runinternal; + temp.RunInternal(); // exits when no bf is found. + return; + } + /* Start a normal shell and check for a first command init */ + WriteOut(MSG_Get("SHELL_STARTUP_BEGIN"),VERSION); +#if C_DEBUG + WriteOut(MSG_Get("SHELL_STARTUP_DEBUG")); +#endif + if (machine == MCH_CGA) WriteOut(MSG_Get("SHELL_STARTUP_CGA")); + WriteOut(MSG_Get("SHELL_STARTUP_END")); + + if (cmd->FindString("/INIT",line,true)) { + strcpy(input_line,line.c_str()); + line.erase(); + ParseLine(input_line); + } + do { + if (bf){ + if(bf->ReadLine(input_line)) { + if (echo) { + if (input_line[0]!='@') { + ShowPrompt(); + WriteOut_NoParsing(input_line); + WriteOut_NoParsing("\n"); + }; + }; + ParseLine(input_line); + if (echo) WriteOut("\n"); + } + } else { + if (echo) ShowPrompt(); + InputCommand(input_line); + ParseLine(input_line); + if (echo && !bf) WriteOut_NoParsing("\n"); + } + } while (!exit); +} + +void DOS_Shell::SyntaxError(void) { + WriteOut(MSG_Get("SHELL_SYNTAXERROR")); +} + +class AUTOEXEC:public Module_base { +private: + AutoexecObject autoexec[17]; + AutoexecObject autoexec_echo; +public: + AUTOEXEC(Section* configuration):Module_base(configuration) { + /* Register a virtual AUOEXEC.BAT file */ + std::string line; + Section_line * section=static_cast(configuration); + + /* Check -securemode switch to disable mount/imgmount/boot after running autoexec.bat */ + bool secure = control->cmdline->FindExist("-securemode",true); + + /* add stuff from the configfile unless -noautexec or -securemode is specified. */ + char * extra = const_cast(section->data.c_str()); + if (extra && !secure && !control->cmdline->FindExist("-noautoexec",true)) { + /* detect if "echo off" is the first line */ + bool echo_off = !strncasecmp(extra,"echo off",8); + if (!echo_off) echo_off = !strncasecmp(extra,"@echo off",9); + + /* if "echo off" add it to the front of autoexec.bat */ + if(echo_off) autoexec_echo.InstallBefore("@echo off"); + + /* Install the stuff from the configfile */ + autoexec[0].Install(section->data); + } + + /* Check to see for extra command line options to be added (before the command specified on commandline) */ + /* Maximum of extra commands: 10 */ + Bitu i = 1; + while (control->cmdline->FindString("-c",line,true) && (i <= 11)) { +#if defined (WIN32) || defined (OS2) + //replace single with double quotes so that mount commands can contain spaces + for(Bitu temp = 0;temp < line.size();++temp) if(line[temp] == '\'') line[temp]='\"'; +#endif //Linux users can simply use \" in their shell + autoexec[i++].Install(line); + } + + /* Check for the -exit switch which causes dosbox to when the command on the commandline has finished */ + bool addexit = control->cmdline->FindExist("-exit",true); + + /* Check for first command being a directory or file */ + char buffer[CROSS_LEN]; + char cross_filesplit[2] = {CROSS_FILESPLIT , 0}; + /* Combining -securemode and no parameter leaves you with a lovely Z:\. */ + if ( !control->cmdline->FindCommand(1,line) ) { + if ( secure ) autoexec[12].Install("z:\\config.com -securemode"); + } else { + struct stat test; + strcpy(buffer,line.c_str()); + if (stat(buffer,&test)){ + getcwd(buffer,CROSS_LEN); + strcat(buffer,cross_filesplit); + strcat(buffer,line.c_str()); + if (stat(buffer,&test)) goto nomount; + } + if (test.st_mode & S_IFDIR) { + autoexec[12].Install(std::string("MOUNT C \"") + buffer + "\""); + autoexec[13].Install("C:"); + if(secure) autoexec[14].Install("z:\\config.com -securemode"); + } else { + char* name = strrchr(buffer,CROSS_FILESPLIT); + if (!name) { //Only a filename + line = buffer; + getcwd(buffer,CROSS_LEN); + strcat(buffer,cross_filesplit); + strcat(buffer,line.c_str()); + if(stat(buffer,&test)) goto nomount; + name = strrchr(buffer,CROSS_FILESPLIT); + if(!name) goto nomount; + } + *name++ = 0; + if (access(buffer,F_OK)) goto nomount; + autoexec[12].Install(std::string("MOUNT C \"") + buffer + "\""); + autoexec[13].Install("C:"); + upcase(name); + if(strstr(name,".BAT") != 0) { + if(secure) autoexec[14].Install("z:\\config.com -securemode"); + /* BATch files are called else exit will not work */ + autoexec[15].Install(std::string("CALL ") + name); + } else if((strstr(name,".IMG") != 0) || (strstr(name,".IMA") !=0)) { + //No secure mode here as boot is destructive and enabling securemode disables boot + /* Boot image files */ + autoexec[15].Install(std::string("BOOT ") + name); + } else { + if(secure) autoexec[14].Install("z:\\config.com -securemode"); + autoexec[15].Install(name); + } + + if(addexit) autoexec[16].Install("exit"); + } + } +nomount: + VFILE_Register("AUTOEXEC.BAT",(Bit8u *)autoexec_data,(Bit32u)strlen(autoexec_data)); + } +}; + +static AUTOEXEC* test; + +void AUTOEXEC_Init(Section * sec) { + test = new AUTOEXEC(sec); +} + +static char const * const path_string="PATH=Z:\\"; +static char const * const comspec_string="COMSPEC=Z:\\COMMAND.COM"; +static char const * const full_name="Z:\\COMMAND.COM"; +static char const * const init_line="/INIT AUTOEXEC.BAT"; + +void SHELL_Init() { + /* Add messages */ + MSG_Add("SHELL_ILLEGAL_PATH","Illegal Path.\n"); + MSG_Add("SHELL_CMD_HELP","If you want a list of all supported commands type \033[33;1mhelp /all\033[0m .\nA short list of the most often used commands:\n"); + MSG_Add("SHELL_CMD_ECHO_ON","ECHO is on.\n"); + MSG_Add("SHELL_CMD_ECHO_OFF","ECHO is off.\n"); + MSG_Add("SHELL_ILLEGAL_SWITCH","Illegal switch: %s.\n"); + MSG_Add("SHELL_MISSING_PARAMETER","Required parameter missing.\n"); + MSG_Add("SHELL_CMD_CHDIR_ERROR","Unable to change to: %s.\n"); + MSG_Add("SHELL_CMD_CHDIR_HINT","To change to different drive type \033[31m%c:\033[0m\n"); + MSG_Add("SHELL_CMD_CHDIR_HINT_2","directoryname is longer than 8 charachters and/or contains spaces.\nTry \033[31mcd %s\033[0m\n"); + MSG_Add("SHELL_CMD_CHDIR_HINT_3","You are still on drive Z:, change to a mounted drive with \033[31mC:\033[0m.\n"); + MSG_Add("SHELL_CMD_MKDIR_ERROR","Unable to make: %s.\n"); + MSG_Add("SHELL_CMD_RMDIR_ERROR","Unable to remove: %s.\n"); + MSG_Add("SHELL_CMD_DEL_ERROR","Unable to delete: %s.\n"); + MSG_Add("SHELL_SYNTAXERROR","The syntax of the command is incorrect.\n"); + MSG_Add("SHELL_CMD_SET_NOT_SET","Environment variable %s not defined.\n"); + MSG_Add("SHELL_CMD_SET_OUT_OF_SPACE","Not enough environment space left.\n"); + MSG_Add("SHELL_CMD_IF_EXIST_MISSING_FILENAME","IF EXIST: Missing filename.\n"); + MSG_Add("SHELL_CMD_IF_ERRORLEVEL_MISSING_NUMBER","IF ERRORLEVEL: Missing number.\n"); + MSG_Add("SHELL_CMD_IF_ERRORLEVEL_INVALID_NUMBER","IF ERRORLEVEL: Invalid number.\n"); + MSG_Add("SHELL_CMD_GOTO_MISSING_LABEL","No label supplied to GOTO command.\n"); + MSG_Add("SHELL_CMD_GOTO_LABEL_NOT_FOUND","GOTO: Label %s not found.\n"); + MSG_Add("SHELL_CMD_FILE_NOT_FOUND","File %s not found.\n"); + MSG_Add("SHELL_CMD_FILE_EXISTS","File %s already exists.\n"); + MSG_Add("SHELL_CMD_DIR_INTRO","Directory of %s.\n"); + MSG_Add("SHELL_CMD_DIR_BYTES_USED","%5d File(s) %17s Bytes.\n"); + MSG_Add("SHELL_CMD_DIR_BYTES_FREE","%5d Dir(s) %17s Bytes free.\n"); + MSG_Add("SHELL_EXECUTE_DRIVE_NOT_FOUND","Drive %c does not exist!\nYou must \033[31mmount\033[0m it first. Type \033[1;33mintro\033[0m or \033[1;33mintro mount\033[0m for more information.\n"); + MSG_Add("SHELL_EXECUTE_ILLEGAL_COMMAND","Illegal command: %s.\n"); + MSG_Add("SHELL_CMD_PAUSE","Press any key to continue.\n"); + MSG_Add("SHELL_CMD_PAUSE_HELP","Waits for 1 keystroke to continue.\n"); + MSG_Add("SHELL_CMD_COPY_FAILURE","Copy failure : %s.\n"); + MSG_Add("SHELL_CMD_COPY_SUCCESS"," %d File(s) copied.\n"); + MSG_Add("SHELL_CMD_SUBST_NO_REMOVE","Removing drive not supported. Doing nothing.\n"); + MSG_Add("SHELL_CMD_SUBST_FAILURE","SUBST failed. You either made an error in your commandline or the target drive is already used.\nIt's only possible to use SUBST on Local drives"); + +#ifdef HW_RVL + MSG_Add("SHELL_STARTUP_BEGIN", + "\033[44;1m========================" + "========================" + "======================\n" + "| \033[32mWelcome to DOSBox v%-8s\033[37m |\n" + "| |\n" +// "| DOSBox runs real and protected mode games. |\n" + "| For a short introduction for new users type: \033[33mINTRO\033[37m |\n" + "| For supported shell commands type: \033[33mHELP\033[37m |\n" + "| |\n" + "| If you want more speed, try \033[31mctrl-F8\033[37m and \033[31mctrl-F12\033[37m. |\n" + "| To activate the keymapper \033[31mctrl-F1\033[37m. |\n" + "| For more information read the \033[36mREADME\033[37m file in the DOSBox directory. |\n" + "| |\n" + ); + MSG_Add("SHELL_STARTUP_CGA","| DOSBox supports Composite CGA mode. |\n" + "| Use \033[31m(alt-)F11\033[37m to change the colours when in this mode. |\n" + "| |\n" + ); + MSG_Add("SHELL_STARTUP_DEBUG", + "| Press \033[31malt-Pause\033[37m to enter the debugger or start the exe with \033[33mDEBUG\033[37m. |\n" + "| |\n" + ); + MSG_Add("SHELL_STARTUP_END", + "| \033[32mHAVE FUN!\033[37m |\n" + "| \033[32mThe DOSBox Team\033[37m |\n" + "========================" + "========================" + "======================\033[0m\n" + //"\n" //Breaks the startup message if you type a mount and a drive change. + ); +#else + MSG_Add("SHELL_STARTUP_BEGIN", + "\033[44;1m\xC9\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBB\n" + "\xBA \033[32mWelcome to DOSBox v%-8s\033[37m \xBA\n" + "\xBA \xBA\n" +// "\xBA DOSBox runs real and protected mode games. \xBA\n" + "\xBA For a short introduction for new users type: \033[33mINTRO\033[37m \xBA\n" + "\xBA For supported shell commands type: \033[33mHELP\033[37m \xBA\n" + "\xBA \xBA\n" + "\xBA If you want more speed, try \033[31mctrl-F8\033[37m and \033[31mctrl-F12\033[37m. \xBA\n" + "\xBA To activate the keymapper \033[31mctrl-F1\033[37m. \xBA\n" + "\xBA For more information read the \033[36mREADME\033[37m file in the DOSBox directory. \xBA\n" + "\xBA \xBA\n" + ); + MSG_Add("SHELL_STARTUP_CGA","\xBA DOSBox supports Composite CGA mode. \xBA\n" + "\xBA Use \033[31m(alt-)F11\033[37m to change the colours when in this mode. \xBA\n" + "\xBA \xBA\n" + ); + MSG_Add("SHELL_STARTUP_DEBUG", + "\xBA Press \033[31malt-Pause\033[37m to enter the debugger or start the exe with \033[33mDEBUG\033[37m. \xBA\n" + "\xBA \xBA\n" + ); + MSG_Add("SHELL_STARTUP_END", + "\xBA \033[32mHAVE FUN!\033[37m \xBA\n" + "\xBA \033[32mThe DOSBox Team \033[33mhttp://www.dosbox.com\033[37m \xBA\n" + "\xC8\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBC\033[0m\n" + //"\n" //Breaks the startup message if you type a mount and a drive change. + ); +#endif + MSG_Add("SHELL_CMD_CHDIR_HELP","Displays/changes the current directory.\n"); + MSG_Add("SHELL_CMD_CHDIR_HELP_LONG","CHDIR [drive:][path]\n" + "CHDIR [..]\n" + "CD [drive:][path]\n" + "CD [..]\n\n" + " .. Specifies that you want to change to the parent directory.\n\n" + "Type CD drive: to display the current directory in the specified drive.\n" + "Type CD without parameters to display the current drive and directory.\n"); + MSG_Add("SHELL_CMD_CLS_HELP","Clear screen.\n"); + MSG_Add("SHELL_CMD_DIR_HELP","Directory View.\n"); + MSG_Add("SHELL_CMD_ECHO_HELP","Display messages and enable/disable command echoing.\n"); + MSG_Add("SHELL_CMD_EXIT_HELP","Exit from the shell.\n"); + MSG_Add("SHELL_CMD_HELP_HELP","Show help.\n"); + MSG_Add("SHELL_CMD_MKDIR_HELP","Make Directory.\n"); + MSG_Add("SHELL_CMD_MKDIR_HELP_LONG","MKDIR [drive:][path]\n" + "MD [drive:][path]\n"); + MSG_Add("SHELL_CMD_RMDIR_HELP","Remove Directory.\n"); + MSG_Add("SHELL_CMD_RMDIR_HELP_LONG","RMDIR [drive:][path]\n" + "RD [drive:][path]\n"); + MSG_Add("SHELL_CMD_SET_HELP","Change environment variables.\n"); + MSG_Add("SHELL_CMD_IF_HELP","Performs conditional processing in batch programs.\n"); + MSG_Add("SHELL_CMD_GOTO_HELP","Jump to a labeled line in a batch script.\n"); + MSG_Add("SHELL_CMD_SHIFT_HELP","Leftshift commandline parameters in a batch script.\n"); + MSG_Add("SHELL_CMD_TYPE_HELP","Display the contents of a text-file.\n"); + MSG_Add("SHELL_CMD_TYPE_HELP_LONG","TYPE [drive:][path][filename]\n"); + MSG_Add("SHELL_CMD_REM_HELP","Add comments in a batch file.\n"); + MSG_Add("SHELL_CMD_REM_HELP_LONG","REM [comment]\n"); + MSG_Add("SHELL_CMD_NO_WILD","This is a simple version of the command, no wildcards allowed!\n"); + MSG_Add("SHELL_CMD_RENAME_HELP","Renames one or more files.\n"); + MSG_Add("SHELL_CMD_RENAME_HELP_LONG","RENAME [drive:][path]filename1 filename2.\n" + "REN [drive:][path]filename1 filename2.\n\n" + "Note that you can not specify a new drive or path for your destination file.\n"); + MSG_Add("SHELL_CMD_DELETE_HELP","Removes one or more files.\n"); + MSG_Add("SHELL_CMD_COPY_HELP","Copy files.\n"); + MSG_Add("SHELL_CMD_CALL_HELP","Start a batch file from within another batch file.\n"); + MSG_Add("SHELL_CMD_SUBST_HELP","Assign an internal directory to a drive.\n"); + MSG_Add("SHELL_CMD_LOADHIGH_HELP","Loads a program into upper memory (requires xms=true,umb=true).\n"); + MSG_Add("SHELL_CMD_CHOICE_HELP","Waits for a keypress and sets ERRORLEVEL.\n"); + MSG_Add("SHELL_CMD_CHOICE_HELP_LONG","CHOICE [/C:choices] [/N] [/S] text\n" + " /C[:]choices - Specifies allowable keys. Default is: yn.\n" + " /N - Do not display the choices at end of prompt.\n" + " /S - Enables case-sensitive choices to be selected.\n" + " text - The text to display as a prompt.\n"); + MSG_Add("SHELL_CMD_ATTRIB_HELP","Does nothing. Provided for compatibility.\n"); + MSG_Add("SHELL_CMD_PATH_HELP","Provided for compatibility.\n"); + MSG_Add("SHELL_CMD_VER_HELP","View and set the reported DOS version.\n"); + MSG_Add("SHELL_CMD_VER_VER","DOSBox version %s. Reported DOS version %d.%02d.\n"); + + /* Regular startup */ + call_shellstop=CALLBACK_Allocate(); + /* Setup the startup CS:IP to kill the last running machine when exitted */ + RealPt newcsip=CALLBACK_RealPointer(call_shellstop); + SegSet16(cs,RealSeg(newcsip)); + reg_ip=RealOff(newcsip); + + CALLBACK_Setup(call_shellstop,shellstop_handler,CB_IRET,"shell stop"); + PROGRAMS_MakeFile("COMMAND.COM",SHELL_ProgramStart); + + /* Now call up the shell for the first time */ + Bit16u psp_seg=DOS_FIRST_SHELL; + Bit16u env_seg=DOS_FIRST_SHELL+19; //DOS_GetMemory(1+(4096/16))+1; + Bit16u stack_seg=DOS_GetMemory(2048/16); + SegSet16(ss,stack_seg); + reg_sp=2046; + + /* Set up int 24 and psp (Telarium games) */ + real_writeb(psp_seg+16+1,0,0xea); /* far jmp */ + real_writed(psp_seg+16+1,1,real_readd(0,0x24*4)); + real_writed(0,0x24*4,((Bit32u)psp_seg<<16) | ((16+1)<<4)); + + /* Set up int 23 to "int 20" in the psp. Fixes what.exe */ + real_writed(0,0x23*4,((Bit32u)psp_seg<<16)); + + /* Setup MCBs */ + DOS_MCB pspmcb((Bit16u)(psp_seg-1)); + pspmcb.SetPSPSeg(psp_seg); // MCB of the command shell psp + pspmcb.SetSize(0x10+2); + pspmcb.SetType(0x4d); + DOS_MCB envmcb((Bit16u)(env_seg-1)); + envmcb.SetPSPSeg(psp_seg); // MCB of the command shell environment + envmcb.SetSize(DOS_MEM_START-env_seg); + envmcb.SetType(0x4d); + + /* Setup environment */ + PhysPt env_write=PhysMake(env_seg,0); + MEM_BlockWrite(env_write,path_string,(Bitu)(strlen(path_string)+1)); + env_write += (PhysPt)(strlen(path_string)+1); + MEM_BlockWrite(env_write,comspec_string,(Bitu)(strlen(comspec_string)+1)); + env_write += (PhysPt)(strlen(comspec_string)+1); + mem_writeb(env_write++,0); + mem_writew(env_write,1); + env_write+=2; + MEM_BlockWrite(env_write,full_name,(Bitu)(strlen(full_name)+1)); + + DOS_PSP psp(psp_seg); + psp.MakeNew(0); + dos.psp(psp_seg); + + /* The start of the filetable in the psp must look like this: + * 01 01 01 00 02 + * In order to achieve this: First open 2 files. Close the first and + * duplicate the second (so the entries get 01) */ + Bit16u dummy=0; + DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDIN */ + DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDOUT */ + DOS_CloseFile(0); /* Close STDIN */ + DOS_ForceDuplicateEntry(1,0); /* "new" STDIN */ + DOS_ForceDuplicateEntry(1,2); /* STDERR */ + DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDAUX */ + DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDPRN */ + + psp.SetParent(psp_seg); + /* Set the environment */ + psp.SetEnvironment(env_seg); + /* Set the command line for the shell start up */ + CommandTail tail; + tail.count=(Bit8u)strlen(init_line); + strcpy(tail.buffer,init_line); + MEM_BlockWrite(PhysMake(psp_seg,128),&tail,128); + + /* Setup internal DOS Variables */ + dos.dta(RealMake(psp_seg,0x80)); + dos.psp(psp_seg); + + + SHELL_ProgramStart(&first_shell); + first_shell->Run(); + delete first_shell; + first_shell = 0;//Make clear that it shouldn't be used anymore +} diff --git a/src/shell/shell_batch.cpp b/src/shell/shell_batch.cpp index f9aaaa8..3d4c166 100644 --- a/src/shell/shell_batch.cpp +++ b/src/shell/shell_batch.cpp @@ -1,202 +1,202 @@ -/* - * Copyright (C) 2002-2009 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_batch.cpp,v 1.36 2009/07/03 19:36:56 qbix79 Exp $ */ - -#include -#include - -#include "shell.h" -#include "support.h" - -BatchFile::BatchFile(DOS_Shell * host,char const * const name, char const * const cmd_line) { - location = 0; - prev=host->bf; - echo=host->echo; - shell=host; - char totalname[DOS_PATHLENGTH+4]; - DOS_Canonicalize(name,totalname); // Get fullname including drive specificiation - cmd = new CommandLine(totalname,cmd_line); - filename = totalname; - - //Test if file is openable - if (!DOS_OpenFile(totalname,128,&file_handle)) { - //TODO Come up with something better - E_Exit("SHELL:Can't open BatchFile %s",totalname); - } - DOS_CloseFile(file_handle); -} - -BatchFile::~BatchFile() { - delete cmd; - shell->bf=prev; - shell->echo=echo; -} - -bool BatchFile::ReadLine(char * line) { - //Open the batchfile and seek to stored postion - if (!DOS_OpenFile(filename.c_str(),128,&file_handle)) { - LOG(LOG_MISC,LOG_ERROR)("ReadLine Can't open BatchFile %s",filename.c_str()); - delete this; - return false; - } - DOS_SeekFile(file_handle,&(this->location),DOS_SEEK_SET); - - Bit8u c=0;Bit16u n=1; - char temp[CMD_MAXLINE]; -emptyline: - char * cmd_write=temp; - do { - n=1; - DOS_ReadFile(file_handle,&c,&n); - if (n>0) { - /* Why are we filtering this ? - * Exclusion list: tab for batch files - * escape for ansi - * backspace for alien odyssey */ - if (c>31 || c==0x1b || c=='\t' || c==8) - *cmd_write++=c; - } - } while (c!='\n' && n); - *cmd_write=0; - if (!n && cmd_write==temp) { - //Close file and delete bat file - DOS_CloseFile(file_handle); - delete this; - return false; - } - if (!strlen(temp)) goto emptyline; - if (temp[0]==':') goto emptyline; - - /* Now parse the line read from the bat file for % stuff */ - cmd_write=line; - char * cmd_read=temp; - char env_name[256];char * env_write; - while (*cmd_read) { - env_write=env_name; - if (*cmd_read=='%') { - cmd_read++; - if (cmd_read[0] == '%') { - cmd_read++; - *cmd_write++='%'; - continue; - } - if (cmd_read[0] == '0') { /* Handle %0 */ - const char *file_name = cmd->GetFileName(); - cmd_read++; - strcpy(cmd_write,file_name); - cmd_write+=strlen(file_name); - continue; - } - char next = cmd_read[0]; - if(next > '0' && next <= '9') { - /* Handle %1 %2 .. %9 */ - cmd_read++; //Progress reader - next -= '0'; - if (cmd->GetCount()<(unsigned int)next) continue; - std::string word; - if (!cmd->FindCommand(next,word)) continue; - strcpy(cmd_write,word.c_str()); - cmd_write+=strlen(word.c_str()); - continue; - } else { - /* Not a command line number has to be an environment */ - char * first=strchr(cmd_read,'%'); - /* No env afterall.Somewhat of a hack though as %% and % aren't handled consistent in dosbox. Maybe echo needs to parse % and %% as well. */ - if (!first) {*cmd_write++ = '%';continue;} - *first++ = 0; - std::string env; - if (shell->GetEnvStr(cmd_read,env)) { - const char * equals=strchr(env.c_str(),'='); - if (!equals) continue; - equals++; - strcpy(cmd_write,equals); - cmd_write+=strlen(equals); - } - cmd_read=first; - } - } else { - *cmd_write++=*cmd_read++; - } - } - *cmd_write=0; - //Store current location and close bat file - this->location = 0; - DOS_SeekFile(file_handle,&(this->location),DOS_SEEK_CUR); - DOS_CloseFile(file_handle); - return true; -} - -bool BatchFile::Goto(char * where) { - //Open bat file and search for the where string - if (!DOS_OpenFile(filename.c_str(),128,&file_handle)) { - LOG(LOG_MISC,LOG_ERROR)("SHELL:Goto Can't open BatchFile %s",filename.c_str()); - delete this; - return false; - } - - char cmd_buffer[CMD_MAXLINE]; - char * cmd_write; - - /* Scan till we have a match or return false */ - Bit8u c;Bit16u n; -again: - cmd_write=cmd_buffer; - do { - n=1; - DOS_ReadFile(file_handle,&c,&n); - if (n>0) { - if (c>31) - *cmd_write++=c; - } - } while (c!='\n' && n); - *cmd_write++ = 0; - char *nospace = trim(cmd_buffer); - if (nospace[0] == ':') { - nospace++; //Skip : - //Strip spaces and = from it. - while(*nospace && (isspace(*reinterpret_cast(nospace)) || (*nospace == '='))) - nospace++; - - //label is until space/=/eol - char* const beginlabel = nospace; - while(*nospace && !isspace(*reinterpret_cast(nospace)) && (*nospace != '=')) - nospace++; - - *nospace = 0; - if (strcasecmp(beginlabel,where)==0) { - //Found it! Store location and continue - this->location = 0; - DOS_SeekFile(file_handle,&(this->location),DOS_SEEK_CUR); - DOS_CloseFile(file_handle); - return true; - } - - } - if (!n) { - DOS_CloseFile(file_handle); - delete this; - return false; - } - goto again; - return false; -} - -void BatchFile::Shift(void) { - cmd->Shift(1); -} +/* + * Copyright (C) 2002-2009 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_batch.cpp,v 1.36 2009-07-03 19:36:56 qbix79 Exp $ */ + +#include +#include + +#include "shell.h" +#include "support.h" + +BatchFile::BatchFile(DOS_Shell * host,char const * const name, char const * const cmd_line) { + location = 0; + prev=host->bf; + echo=host->echo; + shell=host; + char totalname[DOS_PATHLENGTH+4]; + DOS_Canonicalize(name,totalname); // Get fullname including drive specificiation + cmd = new CommandLine(totalname,cmd_line); + filename = totalname; + + //Test if file is openable + if (!DOS_OpenFile(totalname,128,&file_handle)) { + //TODO Come up with something better + E_Exit("SHELL:Can't open BatchFile %s",totalname); + } + DOS_CloseFile(file_handle); +} + +BatchFile::~BatchFile() { + delete cmd; + shell->bf=prev; + shell->echo=echo; +} + +bool BatchFile::ReadLine(char * line) { + //Open the batchfile and seek to stored postion + if (!DOS_OpenFile(filename.c_str(),128,&file_handle)) { + LOG(LOG_MISC,LOG_ERROR)("ReadLine Can't open BatchFile %s",filename.c_str()); + delete this; + return false; + } + DOS_SeekFile(file_handle,&(this->location),DOS_SEEK_SET); + + Bit8u c=0;Bit16u n=1; + char temp[CMD_MAXLINE]; +emptyline: + char * cmd_write=temp; + do { + n=1; + DOS_ReadFile(file_handle,&c,&n); + if (n>0) { + /* Why are we filtering this ? + * Exclusion list: tab for batch files + * escape for ansi + * backspace for alien odyssey */ + if (c>31 || c==0x1b || c=='\t' || c==8) + *cmd_write++=c; + } + } while (c!='\n' && n); + *cmd_write=0; + if (!n && cmd_write==temp) { + //Close file and delete bat file + DOS_CloseFile(file_handle); + delete this; + return false; + } + if (!strlen(temp)) goto emptyline; + if (temp[0]==':') goto emptyline; + + /* Now parse the line read from the bat file for % stuff */ + cmd_write=line; + char * cmd_read=temp; + char env_name[256];char * env_write; + while (*cmd_read) { + env_write=env_name; + if (*cmd_read=='%') { + cmd_read++; + if (cmd_read[0] == '%') { + cmd_read++; + *cmd_write++='%'; + continue; + } + if (cmd_read[0] == '0') { /* Handle %0 */ + const char *file_name = cmd->GetFileName(); + cmd_read++; + strcpy(cmd_write,file_name); + cmd_write+=strlen(file_name); + continue; + } + char next = cmd_read[0]; + if(next > '0' && next <= '9') { + /* Handle %1 %2 .. %9 */ + cmd_read++; //Progress reader + next -= '0'; + if (cmd->GetCount()<(unsigned int)next) continue; + std::string word; + if (!cmd->FindCommand(next,word)) continue; + strcpy(cmd_write,word.c_str()); + cmd_write+=strlen(word.c_str()); + continue; + } else { + /* Not a command line number has to be an environment */ + char * first=strchr(cmd_read,'%'); + /* No env afterall.Somewhat of a hack though as %% and % aren't handled consistent in dosbox. Maybe echo needs to parse % and %% as well. */ + if (!first) {*cmd_write++ = '%';continue;} + *first++ = 0; + std::string env; + if (shell->GetEnvStr(cmd_read,env)) { + const char * equals=strchr(env.c_str(),'='); + if (!equals) continue; + equals++; + strcpy(cmd_write,equals); + cmd_write+=strlen(equals); + } + cmd_read=first; + } + } else { + *cmd_write++=*cmd_read++; + } + } + *cmd_write=0; + //Store current location and close bat file + this->location = 0; + DOS_SeekFile(file_handle,&(this->location),DOS_SEEK_CUR); + DOS_CloseFile(file_handle); + return true; +} + +bool BatchFile::Goto(char * where) { + //Open bat file and search for the where string + if (!DOS_OpenFile(filename.c_str(),128,&file_handle)) { + LOG(LOG_MISC,LOG_ERROR)("SHELL:Goto Can't open BatchFile %s",filename.c_str()); + delete this; + return false; + } + + char cmd_buffer[CMD_MAXLINE]; + char * cmd_write; + + /* Scan till we have a match or return false */ + Bit8u c;Bit16u n; +again: + cmd_write=cmd_buffer; + do { + n=1; + DOS_ReadFile(file_handle,&c,&n); + if (n>0) { + if (c>31) + *cmd_write++=c; + } + } while (c!='\n' && n); + *cmd_write++ = 0; + char *nospace = trim(cmd_buffer); + if (nospace[0] == ':') { + nospace++; //Skip : + //Strip spaces and = from it. + while(*nospace && (isspace(*reinterpret_cast(nospace)) || (*nospace == '='))) + nospace++; + + //label is until space/=/eol + char* const beginlabel = nospace; + while(*nospace && !isspace(*reinterpret_cast(nospace)) && (*nospace != '=')) + nospace++; + + *nospace = 0; + if (strcasecmp(beginlabel,where)==0) { + //Found it! Store location and continue + this->location = 0; + DOS_SeekFile(file_handle,&(this->location),DOS_SEEK_CUR); + DOS_CloseFile(file_handle); + return true; + } + + } + if (!n) { + DOS_CloseFile(file_handle); + delete this; + return false; + } + goto again; + return false; +} + +void BatchFile::Shift(void) { + cmd->Shift(1); +} diff --git a/src/shell/shell_cmds.cpp b/src/shell/shell_cmds.cpp index 1ada9df..e04e018 100644 --- a/src/shell/shell_cmds.cpp +++ b/src/shell/shell_cmds.cpp @@ -1,1092 +1,1092 @@ -/* - * Copyright (C) 2002-2009 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.93 2009/09/21 21:04:25 h-a-l-9000 Exp $ */ - -#include "dosbox.h" -#include "shell.h" -#include "callback.h" -#include "regs.h" -#include "../dos/drives.h" -#include "support.h" -#include "control.h" -#include -#include -#include -#include -#include - -static SHELL_Cmd cmd_list[]={ -{ "CHDIR", 1, &DOS_Shell::CMD_CHDIR, "SHELL_CMD_CHDIR_HELP"}, -{ "CD", 0, &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", 0, &DOS_Shell::CMD_DELETE, "SHELL_CMD_DELETE_HELP"}, -{ "DELETE", 1, &DOS_Shell::CMD_DELETE, "SHELL_CMD_DELETE_HELP"}, -{ "ERASE", 1, &DOS_Shell::CMD_DELETE, "SHELL_CMD_DELETE_HELP"}, -{ "ECHO", 1, &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", 1, &DOS_Shell::CMD_MKDIR, "SHELL_CMD_MKDIR_HELP"}, -{ "MD", 0, &DOS_Shell::CMD_MKDIR, "SHELL_CMD_MKDIR_HELP"}, -{ "RMDIR", 1, &DOS_Shell::CMD_RMDIR, "SHELL_CMD_RMDIR_HELP"}, -{ "RD", 0, &DOS_Shell::CMD_RMDIR, "SHELL_CMD_RMDIR_HELP"}, -{ "SET", 1, &DOS_Shell::CMD_SET, "SHELL_CMD_SET_HELP"}, -{ "IF", 1, &DOS_Shell::CMD_IF, "SHELL_CMD_IF_HELP"}, -{ "GOTO", 1, &DOS_Shell::CMD_GOTO, "SHELL_CMD_GOTO_HELP"}, -{ "SHIFT", 1, &DOS_Shell::CMD_SHIFT, "SHELL_CMD_SHIFT_HELP"}, -{ "TYPE", 0, &DOS_Shell::CMD_TYPE, "SHELL_CMD_TYPE_HELP"}, -{ "REM", 1, &DOS_Shell::CMD_REM, "SHELL_CMD_REM_HELP"}, -{ "RENAME", 1, &DOS_Shell::CMD_RENAME, "SHELL_CMD_RENAME_HELP"}, -{ "REN", 0, &DOS_Shell::CMD_RENAME, "SHELL_CMD_RENAME_HELP"}, -{ "PAUSE", 1, &DOS_Shell::CMD_PAUSE, "SHELL_CMD_PAUSE_HELP"}, -{ "CALL", 1, &DOS_Shell::CMD_CALL, "SHELL_CMD_CALL_HELP"}, -{ "SUBST", 1, &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", 1, &DOS_Shell::CMD_ATTRIB, "SHELL_CMD_ATTRIB_HELP"}, -{ "PATH", 1, &DOS_Shell::CMD_PATH, "SHELL_CMD_PATH_HELP"}, -{ "VER", 0, &DOS_Shell::CMD_VER, "SHELL_CMD_VER_HELP"}, -{0,0,0,0} -}; - -/* support functions */ -static char empty_char = 0; -static char* empty_string = &empty_char; -static void StripSpaces(char*&args) { - while(args && *args && isspace(*reinterpret_cast(args))) - args++; -} - -static void StripSpaces(char*&args,char also) { - while(args && *args && (isspace(*reinterpret_cast(args)) || (*args == also))) - args++; -} - -static char* ExpandDot(char*args, char* buffer) { - if(*args == '.') { - if(*(args+1) == 0){ - strcpy(buffer,"*.*"); - return buffer; - } - if( (*(args+1) != '.') && (*(args+1) != '\\') ) { - buffer[0] = '*'; - buffer[1] = 0; - strcat(buffer,args); - return buffer; - } else - strcpy (buffer, args); - } - else strcpy(buffer,args); - return buffer; -} - - - -bool DOS_Shell::CheckConfig(char* cmd_in,char*line) { - 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:\\config "); - strcat(newcom,test->GetName()); strcat(newcom," "); - strcat(newcom,cmd_in);strcat(newcom,line); - DoCommand(newcom); - return true; -} - -void DOS_Shell::DoCommand(char * line) { -/* First split the line into command and arguments */ - line=trim(line); - char cmd_buffer[CMD_MAXLINE]; - char * cmd_write=cmd_buffer; - while (*line) { - if (*line==32) break; - if (*line=='/') break; - if (*line=='\t') break; - if (*line=='=') 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_buffer)==0) { - (this->*(cmd_list[cmd_index].handler))(line); - return; - } - cmd_index++; - } - } - *cmd_write++=*line++; - } - *cmd_write=0; - if (strlen(cmd_buffer)==0) return; -/* Check the internal list */ - Bit32u cmd_index=0; - while (cmd_list[cmd_index].name) { - if (strcasecmp(cmd_list[cmd_index].name,cmd_buffer)==0) { - (this->*(cmd_list[cmd_index].handler))(line); - return; - } - cmd_index++; - } -/* This isn't an internal command execute it */ - if(Execute(cmd_buffer,line)) return; - if(CheckConfig(cmd_buffer,line)) return; - WriteOut(MSG_Get("SHELL_EXECUTE_ILLEGAL_COMMAND"),cmd_buffer); -} - -#define HELP(command) \ - if (ScanCMDBool(args,"?")) { \ - 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"); \ - return; \ - } - -void DOS_Shell::CMD_CLS(char * args) { - HELP("CLS"); - reg_ax=0x0003; - CALLBACK_RunRealInt(0x10); -} - -void DOS_Shell::CMD_DELETE(char * args) { - HELP("DELETE"); - /* Command uses dta so set it to our internal dta */ - RealPt save_dta=dos.dta(); - dos.dta(dos.tables.tempdta); - - 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); - dos.dta(save_dta); - 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(); - } - dos.dta(save_dta); -} - -void DOS_Shell::CMD_HELP(char * args){ - HELP("HELP"); - bool optall=ScanCMDBool(args,"ALL"); - /* Print the help */ - if(!optall) WriteOut(MSG_Get("SHELL_CMD_HELP")); - Bit32u cmd_index=0,write_count=0; - while (cmd_list[cmd_index].name) { - if (optall || !cmd_list[cmd_index].flags) { - WriteOut("<\033[34;1m%-8s\033[0m> %s",cmd_list[cmd_index].name,MSG_Get(cmd_list[cmd_index].help)); - if(!(++write_count%22)) CMD_PAUSE(empty_string); - } - cmd_index++; - } -} - -void DOS_Shell::CMD_RENAME(char * args){ - HELP("RENAME"); - 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); - char* slash = strrchr(arg1,'\\'); - if(slash) { - slash++; - /* If directory specified (crystal caves installer) - * rename from c:\X : rename c:\abc.exe abc.shr. - * File must appear in C:\ */ - - char dir_source[DOS_PATHLENGTH]={0}; - //Copy first and then modify, makes GCC happy - strcpy(dir_source,arg1); - char* dummy = strrchr(dir_source,'\\'); - *dummy=0; - - if((strlen(dir_source) == 2) && (dir_source[1] == ':')) - strcat(dir_source,"\\"); //X: add slash - - char dir_current[DOS_PATHLENGTH]; - DOS_GetCurrentDir(0,dir_current); - if(!DOS_ChangeDir(dir_source)) { - WriteOut(MSG_Get("SHELL_ILLEGAL_PATH")); - return; - } - DOS_Rename(slash,args); - DOS_ChangeDir(dir_current); - } else { - DOS_Rename(arg1,args); - } -} - -void DOS_Shell::CMD_ECHO(char * args){ - HELP("ECHO"); - 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; - } - 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 allready present. Is this possible?"); - WriteOut("%s\n",args); - } else WriteOut("%s\r\n",args); -} - - -void DOS_Shell::CMD_EXIT(char * args) { - HELP("EXIT"); - exit = true; -} - -void DOS_Shell::CMD_CHDIR(char * args) { - HELP("CHDIR"); - 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(strlen(args) == 2 && args[1]==':') { - WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT"),toupper(*reinterpret_cast(&args[0]))); - } else if (!DOS_ChangeDir(args)) { - /* 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 (temps.size()>8) { - temps.erase(6); - temps += "~1"; - WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT_2"),temps.insert(0,slashpart).c_str()); - } else { - Bit8u drive=DOS_GetDefaultDrive()+'A'; - 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 (!DOS_MakeDir(args)) { - WriteOut(MSG_Get("SHELL_CMD_MKDIR_ERROR"),args); - } -} - -void DOS_Shell::CMD_RMDIR(char * args) { - HELP("RMDIR"); - 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,numg; - numb=num % 1000; - num/=1000; - numk=num % 1000; - num/=1000; - numm=num % 1000; - num/=1000; - numg=num; - if (numg) { - sprintf(buf,"%d,%03d,%03d,%03d",numg,numm,numk,numb); - return; - }; - 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) { - HELP("DIR"); - 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"); - ScanCMDBool(args,"S"); - bool optP=ScanCMDBool(args,"P"); - if (ScanCMDBool(args,"WP") || ScanCMDBool(args,"PW")) { - optW=optP=true; - } - bool optB=ScanCMDBool(args,"B"); - 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); - size_t 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; - if (!optB) WriteOut(MSG_Get("SHELL_CMD_DIR_INTRO"),path); - - /* 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()); - bool ret=DOS_FindFirst(args,0xffff & ~DOS_ATTR_VOLUME); - if (!ret) { - if (!optB) WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args); - dos.dta(save_dta); - 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 = empty_string; - if (!optW && (name[0] != '.')) { - ext = strrchr(name, '.'); - if (!ext) ext = empty_string; - else *ext++ = 0; - } - Bit8u day = (Bit8u)(date & 0x001f); - Bit8u month = (Bit8u)((date >> 5) & 0x000f); - Bit16u year = (Bit16u)((date >> 9) + 1980); - Bit8u hour = (Bit8u)((time >> 5 ) >> 6); - Bit8u minute = (Bit8u)((time >> 5) & 0x003f); - - /* output the file */ - if (optB) { - // this overrides pretty much everything - if (strcmp(".",name) && strcmp("..",name)) { - if ((attr & DOS_ATTR_DIRECTORY)||(strlen(ext)==0)) WriteOut("%s\n",name); - else WriteOut("%s.%s\n",name,ext); - } - } else { - if (attr & DOS_ATTR_DIRECTORY) { - if (optW) { - WriteOut("[%s]",name); - size_t namelen = strlen(name); - if (namelen <= 14) { - for (size_t i=14-namelen;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(empty_string); - } - } - } - } while ( (ret=DOS_FindNext()) ); - if (optW) { - if (w_count%5) WriteOut("\n"); - } - if (!optB) { - /* 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); - } - 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 char defaulttarget[] = "."; - 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()); - Bit32u size;Bit16u date;Bit16u time;Bit8u attr; - char name[DOS_NAMELENGTH_ASCII]; - std::vector 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")) ; - ScanCMDBool(args,"Y"); - ScanCMDBool(args,"-Y"); - - 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 commandline) - // Concatating 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* source_p = NULL; - char source_x[DOS_PATHLENGTH+CROSS_LEN]; - while ( (source_p = StripWord(args)) && *source_p ) { - do { - char* plus = strchr(source_p,'+'); - if (plus) *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; - } - if (!has_drive_spec) { - if (DOS_FindFirst(source_p,0xffff & ~DOS_ATTR_VOLUME)) { - dta.GetResult(name,size,date,time,attr); - if (attr & DOS_ATTR_DIRECTORY && !strstr(source_p,"*.*")) - strcat(source_x,"\\*.*"); - } - } - sources.push_back(copysource(source_x,(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; - Bit32u 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 pathSource[DOS_PATHLENGTH]; - char pathTarget[DOS_PATHLENGTH]; - - if (!DOS_Canonicalize(const_cast(source.filename.c_str()),pathSource)) { - WriteOut(MSG_Get("SHELL_ILLEGAL_PATH")); - dos.dta(save_dta); - return; - } - // cut search pattern - char* pos = strrchr(pathSource,'\\'); - if (pos) *(pos+1) = 0; - - if (!DOS_Canonicalize(const_cast(target.filename.c_str()),pathTarget)) { - WriteOut(MSG_Get("SHELL_ILLEGAL_PATH")); - dos.dta(save_dta); - 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,"\\"); - } - }; - - //Find first sourcefile - bool ret = DOS_FindFirst(const_cast(source.filename.c_str()),0xffff & ~DOS_ATTR_VOLUME); - if (!ret) { - WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),const_cast(source.filename.c_str())); - dos.dta(save_dta); - return; - } - - 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 or open it if in concat mode - strcpy(nameTarget,pathTarget); - if (nameTarget[strlen(nameTarget)-1]=='\\') strcat(nameTarget,name); - - //Don't create a newfile when in concat mode - if (oldsource.concat || DOS_CreateFile(nameTarget,0,&targetHandle)) { - Bit32u dummy=0; - //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 Bit8u buffer[0x8000]; // static, otherwise stack overflow possible. - 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); - if(!source.concat) count++; //Only count concat files once - } else { - DOS_CloseFile(sourceHandle); - WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast(target.filename.c_str())); - } - } else { - DOS_CloseFile(sourceHandle); - WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast(target.filename.c_str())); - } - } else WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast(source.filename.c_str())); - }; - //On the next file - ret = DOS_FindNext(); - }; - } - - WriteOut(MSG_Get("SHELL_CMD_COPY_SUCCESS"),count); - dos.dta(save_dta); -} - -void DOS_Shell::CMD_SET(char * args) { - HELP("SET"); - StripSpaces(args); - std::string line; - if (!*args) { - /* No command line show all environment lines */ - Bitu count=GetEnvCount(); - for (Bitu a=0;a % - } else { - char * second = strchr(++p,'%'); - if(!second) continue; *second++ = 0; - std::string temp; - if (GetEnvStr(p,temp)) { - std::string::size_type equals = temp.find('='); - if (equals == std::string::npos) continue; - strcpy(p_parsed,temp.substr(equals+1).c_str()); - p_parsed += strlen(p_parsed); - } - p = second; - } - } - *p_parsed = 0; - /* Try setting the variable */ - if (!SetEnv(args,parsed)) { - 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(&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; - } - - Bit8u 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 = StripWord(args); - if (!*word) { - WriteOut(MSG_Get("SHELL_CMD_IF_EXIST_MISSING_FILENAME")); - return; - } - - { /* DOS_FindFirst uses dta so set it to our internal dta */ - RealPt save_dta=dos.dta(); - dos.dta(dos.tables.tempdta); - bool ret=DOS_FindFirst(word,0xffff & ~DOS_ATTR_VOLUME); - 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(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(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==':')) 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"); - 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) { - HELP("REM"); -} - -void DOS_Shell::CMD_PAUSE(char * args){ - HELP("PAUSE"); - WriteOut(MSG_Get("SHELL_CMD_PAUSE")); - Bit8u c;Bit16u n=1; - DOS_ReadFile (STDIN,&c,&n); -} - -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_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"); - 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.size()>1) && arg[1] !=':') throw(0); - temp_str[0]=(char)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){ - HELP("LOADHIGH"); - Bit16u umb_start=dos_infoblock.GetStartOfUMBChain(); - Bit8u umb_flag=dos_infoblock.GetUMBChainState(); - Bit8u old_memstrat=(Bit8u)(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); - Bit8u 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); -} - -void DOS_Shell::CMD_CHOICE(char * args){ - HELP("CHOICE"); - static char defchoice[3] = {'y','n',0}; - char *rem = NULL, *ptr; - bool optN = ScanCMDBool(args,"N"); - bool optS = ScanCMDBool(args,"S"); //Case-sensitive matching - ScanCMDBool(args,"T"); //Default Choice after timeout - if (args) { - char *last = strchr(args,0); - StripSpaces(args); - rem = ScanCMDRemain(args); - if (rem && *rem && (tolower(rem[1]) != 'c')) { - WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem); - return; - } - if (args == rem) args = strchr(rem,0)+1; - if (rem) rem += 2; - if(rem && rem[0]==':') rem++; /* optional : after /c */ - if (args > last) args = NULL; - } - if (!rem || !*rem) rem = defchoice; /* No choices specified use YN */ - ptr = rem; - Bit8u c; - if(!optS) while ((c = *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]); - } - - Bit16u n=1; - do { - DOS_ReadFile (STDIN,&c,&n); - } while (!c || !(ptr = strchr(rem,(optS?c:toupper(c))))); - c = optS?c:(Bit8u)toupper(c); - DOS_WriteFile (STDOUT,&c, &n); - dos.return_code = (Bit8u)(ptr-rem+1); -} - -void DOS_Shell::CMD_ATTRIB(char *args){ - HELP("ATTRIB"); - // No-Op for now. -} - -void DOS_Shell::CMD_PATH(char *args){ - HELP("PATH"); - if(args && *args && strlen(args)){ - char pathstring[DOS_PATHLENGTH+CROSS_LEN+20]={ 0 }; - strcpy(pathstring,"set PATH="); - while(args && *args && (*args=='='|| *args==' ')) - args++; - strcat(pathstring,args); - this->ParseLine(pathstring); - return; - } else { - std::string line; - if(GetEnvStr("PATH",line)) { - WriteOut("%s",line.c_str()); - } else { - WriteOut("PATH=(null)"); - } - } -} - -void DOS_Shell::CMD_VER(char *args) { - HELP("VER"); - if(args && *args) { - char* word = StripWord(args); - if(strcasecmp(word,"set")) return; - word = StripWord(args); - dos.version.major = (Bit8u)(atoi(word)); - dos.version.minor = (Bit8u)(atoi(args)); - } else WriteOut(MSG_Get("SHELL_CMD_VER_VER"),VERSION,dos.version.major,dos.version.minor); -} +/* + * Copyright (C) 2002-2009 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.93 2009-09-21 21:04:25 h-a-l-9000 Exp $ */ + +#include "dosbox.h" +#include "shell.h" +#include "callback.h" +#include "regs.h" +#include "../dos/drives.h" +#include "support.h" +#include "control.h" +#include +#include +#include +#include +#include + +static SHELL_Cmd cmd_list[]={ +{ "CHDIR", 1, &DOS_Shell::CMD_CHDIR, "SHELL_CMD_CHDIR_HELP"}, +{ "CD", 0, &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", 0, &DOS_Shell::CMD_DELETE, "SHELL_CMD_DELETE_HELP"}, +{ "DELETE", 1, &DOS_Shell::CMD_DELETE, "SHELL_CMD_DELETE_HELP"}, +{ "ERASE", 1, &DOS_Shell::CMD_DELETE, "SHELL_CMD_DELETE_HELP"}, +{ "ECHO", 1, &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", 1, &DOS_Shell::CMD_MKDIR, "SHELL_CMD_MKDIR_HELP"}, +{ "MD", 0, &DOS_Shell::CMD_MKDIR, "SHELL_CMD_MKDIR_HELP"}, +{ "RMDIR", 1, &DOS_Shell::CMD_RMDIR, "SHELL_CMD_RMDIR_HELP"}, +{ "RD", 0, &DOS_Shell::CMD_RMDIR, "SHELL_CMD_RMDIR_HELP"}, +{ "SET", 1, &DOS_Shell::CMD_SET, "SHELL_CMD_SET_HELP"}, +{ "IF", 1, &DOS_Shell::CMD_IF, "SHELL_CMD_IF_HELP"}, +{ "GOTO", 1, &DOS_Shell::CMD_GOTO, "SHELL_CMD_GOTO_HELP"}, +{ "SHIFT", 1, &DOS_Shell::CMD_SHIFT, "SHELL_CMD_SHIFT_HELP"}, +{ "TYPE", 0, &DOS_Shell::CMD_TYPE, "SHELL_CMD_TYPE_HELP"}, +{ "REM", 1, &DOS_Shell::CMD_REM, "SHELL_CMD_REM_HELP"}, +{ "RENAME", 1, &DOS_Shell::CMD_RENAME, "SHELL_CMD_RENAME_HELP"}, +{ "REN", 0, &DOS_Shell::CMD_RENAME, "SHELL_CMD_RENAME_HELP"}, +{ "PAUSE", 1, &DOS_Shell::CMD_PAUSE, "SHELL_CMD_PAUSE_HELP"}, +{ "CALL", 1, &DOS_Shell::CMD_CALL, "SHELL_CMD_CALL_HELP"}, +{ "SUBST", 1, &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", 1, &DOS_Shell::CMD_ATTRIB, "SHELL_CMD_ATTRIB_HELP"}, +{ "PATH", 1, &DOS_Shell::CMD_PATH, "SHELL_CMD_PATH_HELP"}, +{ "VER", 0, &DOS_Shell::CMD_VER, "SHELL_CMD_VER_HELP"}, +{0,0,0,0} +}; + +/* support functions */ +static char empty_char = 0; +static char* empty_string = &empty_char; +static void StripSpaces(char*&args) { + while(args && *args && isspace(*reinterpret_cast(args))) + args++; +} + +static void StripSpaces(char*&args,char also) { + while(args && *args && (isspace(*reinterpret_cast(args)) || (*args == also))) + args++; +} + +static char* ExpandDot(char*args, char* buffer) { + if(*args == '.') { + if(*(args+1) == 0){ + strcpy(buffer,"*.*"); + return buffer; + } + if( (*(args+1) != '.') && (*(args+1) != '\\') ) { + buffer[0] = '*'; + buffer[1] = 0; + strcat(buffer,args); + return buffer; + } else + strcpy (buffer, args); + } + else strcpy(buffer,args); + return buffer; +} + + + +bool DOS_Shell::CheckConfig(char* cmd_in,char*line) { + 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:\\config "); + strcat(newcom,test->GetName()); strcat(newcom," "); + strcat(newcom,cmd_in);strcat(newcom,line); + DoCommand(newcom); + return true; +} + +void DOS_Shell::DoCommand(char * line) { +/* First split the line into command and arguments */ + line=trim(line); + char cmd_buffer[CMD_MAXLINE]; + char * cmd_write=cmd_buffer; + while (*line) { + if (*line==32) break; + if (*line=='/') break; + if (*line=='\t') break; + if (*line=='=') 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_buffer)==0) { + (this->*(cmd_list[cmd_index].handler))(line); + return; + } + cmd_index++; + } + } + *cmd_write++=*line++; + } + *cmd_write=0; + if (strlen(cmd_buffer)==0) return; +/* Check the internal list */ + Bit32u cmd_index=0; + while (cmd_list[cmd_index].name) { + if (strcasecmp(cmd_list[cmd_index].name,cmd_buffer)==0) { + (this->*(cmd_list[cmd_index].handler))(line); + return; + } + cmd_index++; + } +/* This isn't an internal command execute it */ + if(Execute(cmd_buffer,line)) return; + if(CheckConfig(cmd_buffer,line)) return; + WriteOut(MSG_Get("SHELL_EXECUTE_ILLEGAL_COMMAND"),cmd_buffer); +} + +#define HELP(command) \ + if (ScanCMDBool(args,"?")) { \ + 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"); \ + return; \ + } + +void DOS_Shell::CMD_CLS(char * args) { + HELP("CLS"); + reg_ax=0x0003; + CALLBACK_RunRealInt(0x10); +} + +void DOS_Shell::CMD_DELETE(char * args) { + HELP("DELETE"); + /* Command uses dta so set it to our internal dta */ + RealPt save_dta=dos.dta(); + dos.dta(dos.tables.tempdta); + + 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); + dos.dta(save_dta); + 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(); + } + dos.dta(save_dta); +} + +void DOS_Shell::CMD_HELP(char * args){ + HELP("HELP"); + bool optall=ScanCMDBool(args,"ALL"); + /* Print the help */ + if(!optall) WriteOut(MSG_Get("SHELL_CMD_HELP")); + Bit32u cmd_index=0,write_count=0; + while (cmd_list[cmd_index].name) { + if (optall || !cmd_list[cmd_index].flags) { + WriteOut("<\033[34;1m%-8s\033[0m> %s",cmd_list[cmd_index].name,MSG_Get(cmd_list[cmd_index].help)); + if(!(++write_count%22)) CMD_PAUSE(empty_string); + } + cmd_index++; + } +} + +void DOS_Shell::CMD_RENAME(char * args){ + HELP("RENAME"); + 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); + char* slash = strrchr(arg1,'\\'); + if(slash) { + slash++; + /* If directory specified (crystal caves installer) + * rename from c:\X : rename c:\abc.exe abc.shr. + * File must appear in C:\ */ + + char dir_source[DOS_PATHLENGTH]={0}; + //Copy first and then modify, makes GCC happy + strcpy(dir_source,arg1); + char* dummy = strrchr(dir_source,'\\'); + *dummy=0; + + if((strlen(dir_source) == 2) && (dir_source[1] == ':')) + strcat(dir_source,"\\"); //X: add slash + + char dir_current[DOS_PATHLENGTH]; + DOS_GetCurrentDir(0,dir_current); + if(!DOS_ChangeDir(dir_source)) { + WriteOut(MSG_Get("SHELL_ILLEGAL_PATH")); + return; + } + DOS_Rename(slash,args); + DOS_ChangeDir(dir_current); + } else { + DOS_Rename(arg1,args); + } +} + +void DOS_Shell::CMD_ECHO(char * args){ + HELP("ECHO"); + 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; + } + 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 allready present. Is this possible?"); + WriteOut("%s\n",args); + } else WriteOut("%s\r\n",args); +} + + +void DOS_Shell::CMD_EXIT(char * args) { + HELP("EXIT"); + exit = true; +} + +void DOS_Shell::CMD_CHDIR(char * args) { + HELP("CHDIR"); + 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(strlen(args) == 2 && args[1]==':') { + WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT"),toupper(*reinterpret_cast(&args[0]))); + } else if (!DOS_ChangeDir(args)) { + /* 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 (temps.size()>8) { + temps.erase(6); + temps += "~1"; + WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT_2"),temps.insert(0,slashpart).c_str()); + } else { + Bit8u drive=DOS_GetDefaultDrive()+'A'; + 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 (!DOS_MakeDir(args)) { + WriteOut(MSG_Get("SHELL_CMD_MKDIR_ERROR"),args); + } +} + +void DOS_Shell::CMD_RMDIR(char * args) { + HELP("RMDIR"); + 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,numg; + numb=num % 1000; + num/=1000; + numk=num % 1000; + num/=1000; + numm=num % 1000; + num/=1000; + numg=num; + if (numg) { + sprintf(buf,"%d,%03d,%03d,%03d",numg,numm,numk,numb); + return; + }; + 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) { + HELP("DIR"); + 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"); + ScanCMDBool(args,"S"); + bool optP=ScanCMDBool(args,"P"); + if (ScanCMDBool(args,"WP") || ScanCMDBool(args,"PW")) { + optW=optP=true; + } + bool optB=ScanCMDBool(args,"B"); + 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); + size_t 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; + if (!optB) WriteOut(MSG_Get("SHELL_CMD_DIR_INTRO"),path); + + /* 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()); + bool ret=DOS_FindFirst(args,0xffff & ~DOS_ATTR_VOLUME); + if (!ret) { + if (!optB) WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args); + dos.dta(save_dta); + 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 = empty_string; + if (!optW && (name[0] != '.')) { + ext = strrchr(name, '.'); + if (!ext) ext = empty_string; + else *ext++ = 0; + } + Bit8u day = (Bit8u)(date & 0x001f); + Bit8u month = (Bit8u)((date >> 5) & 0x000f); + Bit16u year = (Bit16u)((date >> 9) + 1980); + Bit8u hour = (Bit8u)((time >> 5 ) >> 6); + Bit8u minute = (Bit8u)((time >> 5) & 0x003f); + + /* output the file */ + if (optB) { + // this overrides pretty much everything + if (strcmp(".",name) && strcmp("..",name)) { + if ((attr & DOS_ATTR_DIRECTORY)||(strlen(ext)==0)) WriteOut("%s\n",name); + else WriteOut("%s.%s\n",name,ext); + } + } else { + if (attr & DOS_ATTR_DIRECTORY) { + if (optW) { + WriteOut("[%s]",name); + size_t namelen = strlen(name); + if (namelen <= 14) { + for (size_t i=14-namelen;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(empty_string); + } + } + } + } while ( (ret=DOS_FindNext()) ); + if (optW) { + if (w_count%5) WriteOut("\n"); + } + if (!optB) { + /* 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); + } + 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 char defaulttarget[] = "."; + 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()); + Bit32u size;Bit16u date;Bit16u time;Bit8u attr; + char name[DOS_NAMELENGTH_ASCII]; + std::vector 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")) ; + ScanCMDBool(args,"Y"); + ScanCMDBool(args,"-Y"); + + 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 commandline) + // Concatating 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* source_p = NULL; + char source_x[DOS_PATHLENGTH+CROSS_LEN]; + while ( (source_p = StripWord(args)) && *source_p ) { + do { + char* plus = strchr(source_p,'+'); + if (plus) *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; + } + if (!has_drive_spec) { + if (DOS_FindFirst(source_p,0xffff & ~DOS_ATTR_VOLUME)) { + dta.GetResult(name,size,date,time,attr); + if (attr & DOS_ATTR_DIRECTORY && !strstr(source_p,"*.*")) + strcat(source_x,"\\*.*"); + } + } + sources.push_back(copysource(source_x,(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; + Bit32u 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 pathSource[DOS_PATHLENGTH]; + char pathTarget[DOS_PATHLENGTH]; + + if (!DOS_Canonicalize(const_cast(source.filename.c_str()),pathSource)) { + WriteOut(MSG_Get("SHELL_ILLEGAL_PATH")); + dos.dta(save_dta); + return; + } + // cut search pattern + char* pos = strrchr(pathSource,'\\'); + if (pos) *(pos+1) = 0; + + if (!DOS_Canonicalize(const_cast(target.filename.c_str()),pathTarget)) { + WriteOut(MSG_Get("SHELL_ILLEGAL_PATH")); + dos.dta(save_dta); + 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,"\\"); + } + }; + + //Find first sourcefile + bool ret = DOS_FindFirst(const_cast(source.filename.c_str()),0xffff & ~DOS_ATTR_VOLUME); + if (!ret) { + WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),const_cast(source.filename.c_str())); + dos.dta(save_dta); + return; + } + + 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 or open it if in concat mode + strcpy(nameTarget,pathTarget); + if (nameTarget[strlen(nameTarget)-1]=='\\') strcat(nameTarget,name); + + //Don't create a newfile when in concat mode + if (oldsource.concat || DOS_CreateFile(nameTarget,0,&targetHandle)) { + Bit32u dummy=0; + //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 Bit8u buffer[0x8000]; // static, otherwise stack overflow possible. + 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); + if(!source.concat) count++; //Only count concat files once + } else { + DOS_CloseFile(sourceHandle); + WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast(target.filename.c_str())); + } + } else { + DOS_CloseFile(sourceHandle); + WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast(target.filename.c_str())); + } + } else WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast(source.filename.c_str())); + }; + //On the next file + ret = DOS_FindNext(); + }; + } + + WriteOut(MSG_Get("SHELL_CMD_COPY_SUCCESS"),count); + dos.dta(save_dta); +} + +void DOS_Shell::CMD_SET(char * args) { + HELP("SET"); + StripSpaces(args); + std::string line; + if (!*args) { + /* No command line show all environment lines */ + Bitu count=GetEnvCount(); + for (Bitu a=0;a % + } else { + char * second = strchr(++p,'%'); + if(!second) continue; *second++ = 0; + std::string temp; + if (GetEnvStr(p,temp)) { + std::string::size_type equals = temp.find('='); + if (equals == std::string::npos) continue; + strcpy(p_parsed,temp.substr(equals+1).c_str()); + p_parsed += strlen(p_parsed); + } + p = second; + } + } + *p_parsed = 0; + /* Try setting the variable */ + if (!SetEnv(args,parsed)) { + 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(&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; + } + + Bit8u 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 = StripWord(args); + if (!*word) { + WriteOut(MSG_Get("SHELL_CMD_IF_EXIST_MISSING_FILENAME")); + return; + } + + { /* DOS_FindFirst uses dta so set it to our internal dta */ + RealPt save_dta=dos.dta(); + dos.dta(dos.tables.tempdta); + bool ret=DOS_FindFirst(word,0xffff & ~DOS_ATTR_VOLUME); + 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(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(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==':')) 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"); + 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) { + HELP("REM"); +} + +void DOS_Shell::CMD_PAUSE(char * args){ + HELP("PAUSE"); + WriteOut(MSG_Get("SHELL_CMD_PAUSE")); + Bit8u c;Bit16u n=1; + DOS_ReadFile (STDIN,&c,&n); +} + +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_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"); + 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.size()>1) && arg[1] !=':') throw(0); + temp_str[0]=(char)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){ + HELP("LOADHIGH"); + Bit16u umb_start=dos_infoblock.GetStartOfUMBChain(); + Bit8u umb_flag=dos_infoblock.GetUMBChainState(); + Bit8u old_memstrat=(Bit8u)(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); + Bit8u 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); +} + +void DOS_Shell::CMD_CHOICE(char * args){ + HELP("CHOICE"); + static char defchoice[3] = {'y','n',0}; + char *rem = NULL, *ptr; + bool optN = ScanCMDBool(args,"N"); + bool optS = ScanCMDBool(args,"S"); //Case-sensitive matching + ScanCMDBool(args,"T"); //Default Choice after timeout + if (args) { + char *last = strchr(args,0); + StripSpaces(args); + rem = ScanCMDRemain(args); + if (rem && *rem && (tolower(rem[1]) != 'c')) { + WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem); + return; + } + if (args == rem) args = strchr(rem,0)+1; + if (rem) rem += 2; + if(rem && rem[0]==':') rem++; /* optional : after /c */ + if (args > last) args = NULL; + } + if (!rem || !*rem) rem = defchoice; /* No choices specified use YN */ + ptr = rem; + Bit8u c; + if(!optS) while ((c = *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]); + } + + Bit16u n=1; + do { + DOS_ReadFile (STDIN,&c,&n); + } while (!c || !(ptr = strchr(rem,(optS?c:toupper(c))))); + c = optS?c:(Bit8u)toupper(c); + DOS_WriteFile (STDOUT,&c, &n); + dos.return_code = (Bit8u)(ptr-rem+1); +} + +void DOS_Shell::CMD_ATTRIB(char *args){ + HELP("ATTRIB"); + // No-Op for now. +} + +void DOS_Shell::CMD_PATH(char *args){ + HELP("PATH"); + if(args && *args && strlen(args)){ + char pathstring[DOS_PATHLENGTH+CROSS_LEN+20]={ 0 }; + strcpy(pathstring,"set PATH="); + while(args && *args && (*args=='='|| *args==' ')) + args++; + strcat(pathstring,args); + this->ParseLine(pathstring); + return; + } else { + std::string line; + if(GetEnvStr("PATH",line)) { + WriteOut("%s",line.c_str()); + } else { + WriteOut("PATH=(null)"); + } + } +} + +void DOS_Shell::CMD_VER(char *args) { + HELP("VER"); + if(args && *args) { + char* word = StripWord(args); + if(strcasecmp(word,"set")) return; + word = StripWord(args); + dos.version.major = (Bit8u)(atoi(word)); + dos.version.minor = (Bit8u)(atoi(args)); + } else WriteOut(MSG_Get("SHELL_CMD_VER_VER"),VERSION,dos.version.major,dos.version.minor); +} diff --git a/src/shell/shell_misc.cpp b/src/shell/shell_misc.cpp index 8ebedae..0f51905 100644 --- a/src/shell/shell_misc.cpp +++ b/src/shell/shell_misc.cpp @@ -1,585 +1,585 @@ -/* - * Copyright (C) 2002-2009 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_misc.cpp,v 1.54 2009/05/27 09:15:42 qbix79 Exp $ */ - -#include -#include -#include //std::copy -#include //std::front_inserter -#include "shell.h" -#include "regs.h" -#include "callback.h" -#include "support.h" - -void DOS_Shell::ShowPrompt(void) { - Bit8u drive=DOS_GetDefaultDrive()+'A'; - char dir[DOS_PATHLENGTH]; - dir[0] = 0; //DOS_GetCurrentDir doesn't always return something. (if drive is messed up) - DOS_GetCurrentDir(0,dir); - WriteOut("%c:\\%s>",drive,dir); -} - -static void outc(Bit8u c) { - Bit16u n=1; - DOS_WriteFile(STDOUT,&c,&n); -} - -void DOS_Shell::InputCommand(char * line) { - Bitu size=CMD_MAXLINE-2; //lastcharacter+0 - Bit8u c;Bit16u n=1; - Bitu str_len=0;Bitu str_index=0; - Bit16u len=0; - bool current_hist=false; // current command stored in history? - - line[0] = '\0'; - - std::list::iterator it_history = l_history.begin(), it_completion = l_completion.begin(); - - while (size) { - dos.echo=false; - 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!"); - } - if (!n) { - size=0; //Kill the while loop - continue; - } - switch (c) { - case 0x00: /* Extended Keys */ - { - DOS_ReadFile(input_handle,&c,&n); - switch (c) { - - case 0x3d: /* F3 */ - 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; - DOS_WriteFile(STDOUT,&c,&n); - } - str_len = str_index = (Bitu)it_history->length(); - size = CMD_MAXLINE - str_index - 2; - line[str_len] = 0; - } - break; - - case 0x4B: /* LEFT */ - if (str_index) { - outc(8); - str_index --; - } - break; - - case 0x4D: /* RIGHT */ - if (str_index < str_len) { - outc(line[str_index++]); - } - break; - - case 0x47: /* HOME */ - while (str_index) { - outc(8); - str_index--; - } - break; - - case 0x4F: /* END */ - while (str_index < str_len) { - outc(line[str_index++]); - } - break; - - case 0x48: /* UP */ - if (l_history.empty() || it_history == l_history.end()) break; - - // store current command in history if we are at beginning - if (it_history == l_history.begin() && !current_hist) { - current_hist=true; - l_history.push_front(line); - } - - for (;str_index>0; str_index--) { - // removes all characters - outc(8); outc(' '); outc(8); - } - strcpy(line, it_history->c_str()); - len = (Bit16u)it_history->length(); - str_len = str_index = len; - size = CMD_MAXLINE - str_index - 2; - 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 ++; - - // remove current command from history - if (current_hist) { - current_hist=false; - l_history.pop_front(); - } - 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 = (Bit16u)it_history->length(); - str_len = str_index = len; - size = CMD_MAXLINE - str_index - 2; - DOS_WriteFile(STDOUT, (Bit8u *)line, &len); - it_history ++; - - break; - case 0x53:/* DELETE */ - { - if(str_index>=str_len) break; - Bit16u a=str_len-str_index-1; - Bit8u* text=reinterpret_cast(&line[str_index+1]); - DOS_WriteFile(STDOUT,text,&a);//write buffer to screen - outc(' ');outc(8); - for(Bitu i=str_index;i0) && (dot_pos-fs_pos>0) && (dot_pos-cl_pos>0)) - strcat(mask, "*"); - else strcat(mask, "*.*"); - } else { - strcpy(mask, "*.*"); - } - - RealPt save_dta=dos.dta(); - dos.dta(dos.tables.tempdta); - - bool res = DOS_FindFirst(mask, 0xffff & ~DOS_ATTR_VOLUME); - if (!res) { - dos.dta(save_dta); - break; // TODO: beep - } - - DOS_DTA dta(dos.dta()); - char name[DOS_NAMELENGTH_ASCII];Bit32u sz;Bit16u date;Bit16u time;Bit8u att; - - std::list executable; - while (res) { - dta.GetResult(name,sz,date,time,att); - // add result to completion list - - char *ext; // file extension - if (strcmp(name, ".") && strcmp(name, "..")) { - if (dir_only) { //Handle the dir only case different (line starts with cd) - if(att & DOS_ATTR_DIRECTORY) l_completion.push_back(name); - } else { - ext = strrchr(name, '.'); - if (ext && (strcmp(ext, ".BAT") == 0 || strcmp(ext, ".COM") == 0 || strcmp(ext, ".EXE") == 0)) - // we add executables to the a seperate list and place that list infront of the normal files - executable.push_front(name); - else - l_completion.push_back(name); - } - } - res=DOS_FindNext(); - } - /* Add excutable list to front of completion list. */ - std::copy(executable.begin(),executable.end(),std::front_inserter(l_completion)); - it_completion = l_completion.begin(); - dos.dta(save_dta); - } - - 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 = (Bit16u)it_completion->length(); - str_len = str_index = completion_index + len; - size = CMD_MAXLINE - str_index - 2; - 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; - default: - if (l_completion.size()) l_completion.clear(); - 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(&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--; - }; - - line[str_index]=c; - str_index ++; - if (str_index > str_len){ - line[str_index] = '\0'; - str_len++; - size--; - } - DOS_WriteFile(STDOUT,&c,&n); - break; - } - } - - if (!str_len) return; - str_len++; - - // remove current command from history if it's there - if (current_hist) { - current_hist=false; - l_history.pop_front(); - } - - // add command line to history - l_history.push_front(line); it_history = l_history.begin(); - if (l_completion.size()) l_completion.clear(); -} - -std::string full_arguments = ""; -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 */ - char fullname[DOS_PATHLENGTH+4]; //stores results from Which - char* p_fullname; - char line[CMD_MAXLINE]; - if(strlen(args)!= 0){ - if(*args != ' '){ //put a space in front - line[0]=' ';line[1]=0; - strncat(line,args,CMD_MAXLINE-2); - line[CMD_MAXLINE-1]=0; - } - else - { - safe_strncpy(line,args,CMD_MAXLINE); - } - }else{ - line[0]=0; - }; - - /* check for a drive change */ - if (((strcmp(name + 1, ":") == 0) || (strcmp(name + 1, ":\\") == 0)) && isalpha(*name)) - { - if (!DOS_SetDrive(toupper(name[0])-'A')) { - WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_NOT_FOUND"),toupper(name[0])); - } - return true; - } - /* Check for a full name */ - p_fullname = Which(name); - if (!p_fullname) return false; - strcpy(fullname,p_fullname); - const 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) - { - //Check if the result will fit in the parameters. Else abort - if(strlen(fullname) >( DOS_PATHLENGTH - 1) ) return false; - char temp_name[DOS_PATHLENGTH+4],* temp_fullname; - //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 - { - return false; - } - - } - } - } - - 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; - bf=new BatchFile(this,fullname,line); - echo=temp_echo; //restore it. - } - else - { /* only .bat .exe .com extensions maybe be executed by the shell */ - if(strcasecmp(extension, ".com") !=0) - { - if(strcasecmp(extension, ".exe") !=0) return false; - } - /* 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); - block.Clear(); - //Add a filename - RealPt file_name=RealMakeSeg(ss,reg_sp+0x20); - MEM_BlockWrite(Real2Phys(file_name),fullname,(Bitu)(strlen(fullname)+1)); - - /* HACK: Store full commandline for mount and imgmount */ - full_arguments.assign(line); - - /* Fill the command line */ - CommandTail cmdtail; - cmdtail.count = 0; - memset(&cmdtail.buffer,0,126); //Else some part of the string is unitialized (valgrind) - if (strlen(line)>126) line[126]=0; - cmdtail.count=(Bit8u)strlen(line); - memcpy(cmdtail.buffer,line,strlen(line)); - cmdtail.buffer[strlen(line)]=0xd; - /* Copy command line in stack block too */ - MEM_BlockWrite(SegPhys(ss)+reg_sp+0x100,&cmdtail,128); - /* Parse FCB (first two parameters) and put them into the current DOS_PSP */ - Bit8u add; - FCB_Parsename(dos.psp(),0x5C,0x00,cmdtail.buffer,&add); - FCB_Parsename(dos.psp(),0x6C,0x00,&cmdtail.buffer[add],&add); - block.exec.fcb1=RealMake(dos.psp(),0x5C); - block.exec.fcb2=RealMake(dos.psp(),0x6C); - /* Set the command line in the block and save it */ - block.exec.cmdtail=RealMakeSeg(ss,reg_sp+0x100); - block.SaveData(); -#if 0 - /* Save CS:IP to some point where i can return them from */ - Bit32u oldeip=reg_eip; - Bit16u oldcs=SegValue(cs); - RealPt newcsip=CALLBACK_RealPointer(call_shellstop); - SegSet16(cs,RealSeg(newcsip)); - reg_ip=RealOff(newcsip); -#endif - /* 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; - SETFLAGBIT(IF,false); - CALLBACK_RunRealInt(0x21); - /* Restore CS:IP and the stack */ - reg_sp+=0x200; -#if 0 - reg_eip=oldeip; - SegSet16(cs,oldcs); -#endif - } - return true; //Executable started -} - - - - -static const char * bat_ext=".BAT"; -static const char * com_ext=".COM"; -static const char * exe_ext=".EXE"; -static char which_ret[DOS_PATHLENGTH+4]; - -char * DOS_Shell::Which(char * name) { - size_t name_len = strlen(name); - if(name_len >= DOS_PATHLENGTH) return 0; - - /* Parse through the Path to find the correct entry */ - /* Check if name is already ok but just misses an extension */ - - 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; - - - /* No Path in filename look through path environment string */ - char path[DOS_PATHLENGTH];std::string temp; - if (!GetEnvStr("PATH",temp)) return 0; - const char * pathenv=temp.c_str(); - if (!pathenv) return 0; - pathenv=strchr(pathenv,'='); - if (!pathenv) return 0; - pathenv++; - Bitu i_path = 0; - while (*pathenv) { - /* remove ; and ;; at the beginning. (and from the second entry etc) */ - while(*pathenv && (*pathenv ==';')) - pathenv++; - - /* get next entry */ - 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; - - - /* check entry */ - if(size_t len = strlen(path)){ - if(len >= (DOS_PATHLENGTH - 2)) continue; - - if(path[len - 1] != '\\') { - strcat(path,"\\"); - len++; - } - - //If name too long =>next - if((name_len + len + 1) >= DOS_PATHLENGTH) continue; - strcat(path,name); - - strcpy(which_ret,path); - 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; - } - } - return 0; -} +/* + * Copyright (C) 2002-2009 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_misc.cpp,v 1.54 2009-05-27 09:15:42 qbix79 Exp $ */ + +#include +#include +#include //std::copy +#include //std::front_inserter +#include "shell.h" +#include "regs.h" +#include "callback.h" +#include "support.h" + +void DOS_Shell::ShowPrompt(void) { + Bit8u drive=DOS_GetDefaultDrive()+'A'; + char dir[DOS_PATHLENGTH]; + dir[0] = 0; //DOS_GetCurrentDir doesn't always return something. (if drive is messed up) + DOS_GetCurrentDir(0,dir); + WriteOut("%c:\\%s>",drive,dir); +} + +static void outc(Bit8u c) { + Bit16u n=1; + DOS_WriteFile(STDOUT,&c,&n); +} + +void DOS_Shell::InputCommand(char * line) { + Bitu size=CMD_MAXLINE-2; //lastcharacter+0 + Bit8u c;Bit16u n=1; + Bitu str_len=0;Bitu str_index=0; + Bit16u len=0; + bool current_hist=false; // current command stored in history? + + line[0] = '\0'; + + std::list::iterator it_history = l_history.begin(), it_completion = l_completion.begin(); + + while (size) { + dos.echo=false; + 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!"); + } + if (!n) { + size=0; //Kill the while loop + continue; + } + switch (c) { + case 0x00: /* Extended Keys */ + { + DOS_ReadFile(input_handle,&c,&n); + switch (c) { + + case 0x3d: /* F3 */ + 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; + DOS_WriteFile(STDOUT,&c,&n); + } + str_len = str_index = (Bitu)it_history->length(); + size = CMD_MAXLINE - str_index - 2; + line[str_len] = 0; + } + break; + + case 0x4B: /* LEFT */ + if (str_index) { + outc(8); + str_index --; + } + break; + + case 0x4D: /* RIGHT */ + if (str_index < str_len) { + outc(line[str_index++]); + } + break; + + case 0x47: /* HOME */ + while (str_index) { + outc(8); + str_index--; + } + break; + + case 0x4F: /* END */ + while (str_index < str_len) { + outc(line[str_index++]); + } + break; + + case 0x48: /* UP */ + if (l_history.empty() || it_history == l_history.end()) break; + + // store current command in history if we are at beginning + if (it_history == l_history.begin() && !current_hist) { + current_hist=true; + l_history.push_front(line); + } + + for (;str_index>0; str_index--) { + // removes all characters + outc(8); outc(' '); outc(8); + } + strcpy(line, it_history->c_str()); + len = (Bit16u)it_history->length(); + str_len = str_index = len; + size = CMD_MAXLINE - str_index - 2; + 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 ++; + + // remove current command from history + if (current_hist) { + current_hist=false; + l_history.pop_front(); + } + 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 = (Bit16u)it_history->length(); + str_len = str_index = len; + size = CMD_MAXLINE - str_index - 2; + DOS_WriteFile(STDOUT, (Bit8u *)line, &len); + it_history ++; + + break; + case 0x53:/* DELETE */ + { + if(str_index>=str_len) break; + Bit16u a=str_len-str_index-1; + Bit8u* text=reinterpret_cast(&line[str_index+1]); + DOS_WriteFile(STDOUT,text,&a);//write buffer to screen + outc(' ');outc(8); + for(Bitu i=str_index;i0) && (dot_pos-fs_pos>0) && (dot_pos-cl_pos>0)) + strcat(mask, "*"); + else strcat(mask, "*.*"); + } else { + strcpy(mask, "*.*"); + } + + RealPt save_dta=dos.dta(); + dos.dta(dos.tables.tempdta); + + bool res = DOS_FindFirst(mask, 0xffff & ~DOS_ATTR_VOLUME); + if (!res) { + dos.dta(save_dta); + break; // TODO: beep + } + + DOS_DTA dta(dos.dta()); + char name[DOS_NAMELENGTH_ASCII];Bit32u sz;Bit16u date;Bit16u time;Bit8u att; + + std::list executable; + while (res) { + dta.GetResult(name,sz,date,time,att); + // add result to completion list + + char *ext; // file extension + if (strcmp(name, ".") && strcmp(name, "..")) { + if (dir_only) { //Handle the dir only case different (line starts with cd) + if(att & DOS_ATTR_DIRECTORY) l_completion.push_back(name); + } else { + ext = strrchr(name, '.'); + if (ext && (strcmp(ext, ".BAT") == 0 || strcmp(ext, ".COM") == 0 || strcmp(ext, ".EXE") == 0)) + // we add executables to the a seperate list and place that list infront of the normal files + executable.push_front(name); + else + l_completion.push_back(name); + } + } + res=DOS_FindNext(); + } + /* Add excutable list to front of completion list. */ + std::copy(executable.begin(),executable.end(),std::front_inserter(l_completion)); + it_completion = l_completion.begin(); + dos.dta(save_dta); + } + + 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 = (Bit16u)it_completion->length(); + str_len = str_index = completion_index + len; + size = CMD_MAXLINE - str_index - 2; + 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; + default: + if (l_completion.size()) l_completion.clear(); + 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(&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--; + }; + + line[str_index]=c; + str_index ++; + if (str_index > str_len){ + line[str_index] = '\0'; + str_len++; + size--; + } + DOS_WriteFile(STDOUT,&c,&n); + break; + } + } + + if (!str_len) return; + str_len++; + + // remove current command from history if it's there + if (current_hist) { + current_hist=false; + l_history.pop_front(); + } + + // add command line to history + l_history.push_front(line); it_history = l_history.begin(); + if (l_completion.size()) l_completion.clear(); +} + +std::string full_arguments = ""; +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 */ + char fullname[DOS_PATHLENGTH+4]; //stores results from Which + char* p_fullname; + char line[CMD_MAXLINE]; + if(strlen(args)!= 0){ + if(*args != ' '){ //put a space in front + line[0]=' ';line[1]=0; + strncat(line,args,CMD_MAXLINE-2); + line[CMD_MAXLINE-1]=0; + } + else + { + safe_strncpy(line,args,CMD_MAXLINE); + } + }else{ + line[0]=0; + }; + + /* check for a drive change */ + if (((strcmp(name + 1, ":") == 0) || (strcmp(name + 1, ":\\") == 0)) && isalpha(*name)) + { + if (!DOS_SetDrive(toupper(name[0])-'A')) { + WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_NOT_FOUND"),toupper(name[0])); + } + return true; + } + /* Check for a full name */ + p_fullname = Which(name); + if (!p_fullname) return false; + strcpy(fullname,p_fullname); + const 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) + { + //Check if the result will fit in the parameters. Else abort + if(strlen(fullname) >( DOS_PATHLENGTH - 1) ) return false; + char temp_name[DOS_PATHLENGTH+4],* temp_fullname; + //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 + { + return false; + } + + } + } + } + + 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; + bf=new BatchFile(this,fullname,line); + echo=temp_echo; //restore it. + } + else + { /* only .bat .exe .com extensions maybe be executed by the shell */ + if(strcasecmp(extension, ".com") !=0) + { + if(strcasecmp(extension, ".exe") !=0) return false; + } + /* 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); + block.Clear(); + //Add a filename + RealPt file_name=RealMakeSeg(ss,reg_sp+0x20); + MEM_BlockWrite(Real2Phys(file_name),fullname,(Bitu)(strlen(fullname)+1)); + + /* HACK: Store full commandline for mount and imgmount */ + full_arguments.assign(line); + + /* Fill the command line */ + CommandTail cmdtail; + cmdtail.count = 0; + memset(&cmdtail.buffer,0,126); //Else some part of the string is unitialized (valgrind) + if (strlen(line)>126) line[126]=0; + cmdtail.count=(Bit8u)strlen(line); + memcpy(cmdtail.buffer,line,strlen(line)); + cmdtail.buffer[strlen(line)]=0xd; + /* Copy command line in stack block too */ + MEM_BlockWrite(SegPhys(ss)+reg_sp+0x100,&cmdtail,128); + /* Parse FCB (first two parameters) and put them into the current DOS_PSP */ + Bit8u add; + FCB_Parsename(dos.psp(),0x5C,0x00,cmdtail.buffer,&add); + FCB_Parsename(dos.psp(),0x6C,0x00,&cmdtail.buffer[add],&add); + block.exec.fcb1=RealMake(dos.psp(),0x5C); + block.exec.fcb2=RealMake(dos.psp(),0x6C); + /* Set the command line in the block and save it */ + block.exec.cmdtail=RealMakeSeg(ss,reg_sp+0x100); + block.SaveData(); +#if 0 + /* Save CS:IP to some point where i can return them from */ + Bit32u oldeip=reg_eip; + Bit16u oldcs=SegValue(cs); + RealPt newcsip=CALLBACK_RealPointer(call_shellstop); + SegSet16(cs,RealSeg(newcsip)); + reg_ip=RealOff(newcsip); +#endif + /* 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; + SETFLAGBIT(IF,false); + CALLBACK_RunRealInt(0x21); + /* Restore CS:IP and the stack */ + reg_sp+=0x200; +#if 0 + reg_eip=oldeip; + SegSet16(cs,oldcs); +#endif + } + return true; //Executable started +} + + + + +static const char * bat_ext=".BAT"; +static const char * com_ext=".COM"; +static const char * exe_ext=".EXE"; +static char which_ret[DOS_PATHLENGTH+4]; + +char * DOS_Shell::Which(char * name) { + size_t name_len = strlen(name); + if(name_len >= DOS_PATHLENGTH) return 0; + + /* Parse through the Path to find the correct entry */ + /* Check if name is already ok but just misses an extension */ + + 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; + + + /* No Path in filename look through path environment string */ + char path[DOS_PATHLENGTH];std::string temp; + if (!GetEnvStr("PATH",temp)) return 0; + const char * pathenv=temp.c_str(); + if (!pathenv) return 0; + pathenv=strchr(pathenv,'='); + if (!pathenv) return 0; + pathenv++; + Bitu i_path = 0; + while (*pathenv) { + /* remove ; and ;; at the beginning. (and from the second entry etc) */ + while(*pathenv && (*pathenv ==';')) + pathenv++; + + /* get next entry */ + 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; + + + /* check entry */ + if(size_t len = strlen(path)){ + if(len >= (DOS_PATHLENGTH - 2)) continue; + + if(path[len - 1] != '\\') { + strcat(path,"\\"); + len++; + } + + //If name too long =>next + if((name_len + len + 1) >= DOS_PATHLENGTH) continue; + strcat(path,name); + + strcpy(which_ret,path); + 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; + } + } + return 0; +}