DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/shell/shell_batch.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #include <stdlib.h>
00021 #include <string.h>
00022 
00023 #include "shell.h"
00024 #include "support.h"
00025 
00026 BatchFile::BatchFile(DOS_Shell * host,char const * const resolved_name,char const * const entered_name, char const * const cmd_line) {
00027         location = 0;
00028         prev=host->bf;
00029         echo=host->echo;
00030         shell=host;
00031         char totalname[DOS_PATHLENGTH+4];
00032         DOS_Canonicalize(resolved_name,totalname); // Get fullname including drive specificiation
00033         cmd = new CommandLine(entered_name,cmd_line);
00034         filename = totalname;
00035 
00036         //Test if file is openable
00037         if (!DOS_OpenFile(totalname,(DOS_NOT_INHERIT|OPEN_READ),&file_handle)) {
00038                 //TODO Come up with something better
00039                 E_Exit("SHELL:Can't open BatchFile %s",totalname);
00040         }
00041         DOS_CloseFile(file_handle);
00042 }
00043 
00044 BatchFile::~BatchFile() {
00045         delete cmd;
00046         shell->bf=prev;
00047         shell->echo=echo;
00048 }
00049 
00050 bool BatchFile::ReadLine(char * line) {
00051         //Open the batchfile and seek to stored postion
00052         if (!DOS_OpenFile(filename.c_str(),(DOS_NOT_INHERIT|OPEN_READ),&file_handle)) {
00053                 LOG(LOG_MISC,LOG_ERROR)("ReadLine Can't open BatchFile %s",filename.c_str());
00054                 delete this;
00055                 return false;
00056         }
00057         DOS_SeekFile(file_handle,&(this->location),DOS_SEEK_SET);
00058 
00059         Bit8u c=0;Bit16u n=1;
00060         char temp[CMD_MAXLINE];
00061 emptyline:
00062         char * cmd_write=temp;
00063         do {
00064                 n=1;
00065                 DOS_ReadFile(file_handle,&c,&n);
00066                 if (n>0) {
00067                         /* Why are we filtering this ?
00068                          * Exclusion list: tab for batch files 
00069                          * escape for ansi
00070                          * backspace for alien odyssey */
00071                         if (c>31 || c==0x1b || c=='\t' || c==8) {
00072                                 //Only add it if room for it (and trailing zero) in the buffer, but do the check here instead at the end
00073                                 //So we continue reading till EOL/EOF
00074                                 if (((cmd_write - temp) + 1) < (CMD_MAXLINE - 1))
00075                                         *cmd_write++ = (char)c;
00076                         } else {
00077                             if (c != '\n' && c != '\r')
00078                                         shell->WriteOut(MSG_Get("SHELL_ILLEGAL_CONTROL_CHARACTER"), c, c);
00079                         }
00080                 }
00081         } while (c!='\n' && n);
00082         *cmd_write=0;
00083         if (!n && cmd_write==temp) {
00084                 //Close file and delete bat file
00085                 DOS_CloseFile(file_handle);
00086                 delete this;
00087                 return false;   
00088         }
00089         if (!strlen(temp)) goto emptyline;
00090         if (temp[0]==':') goto emptyline;
00091 
00092         /* Now parse the line read from the bat file for % stuff */
00093         cmd_write=line;
00094         char * cmd_read=temp;
00095         while (*cmd_read) {
00096                 if (*cmd_read == '%') {
00097                         cmd_read++;
00098                         if (cmd_read[0] == '%') {
00099                                 cmd_read++;
00100                                 if (((cmd_write - line) + 1) < (CMD_MAXLINE - 1))
00101                                         *cmd_write++ = '%';
00102                                 continue;
00103                         }
00104                         if (cmd_read[0] == '0') {  /* Handle %0 */
00105                                 const char *file_name = cmd->GetFileName();
00106                                 cmd_read++;
00107                                 size_t name_len = strlen(file_name);
00108                                 if (((size_t)(cmd_write - line) + name_len) < (CMD_MAXLINE - 1)) {
00109                                         strcpy(cmd_write,file_name);
00110                                         cmd_write += name_len;
00111                                 }
00112                                 continue;
00113                         }
00114                         char next = cmd_read[0];
00115                         if(next > '0' && next <= '9') {
00116                                 /* Handle %1 %2 .. %9 */
00117                                 cmd_read++; //Progress reader
00118                                 next -= '0';
00119                                 if (cmd->GetCount()<(unsigned int)next) continue;
00120                                 std::string word;
00121                                 if (!cmd->FindCommand((unsigned int)next,word)) continue;
00122                                 size_t name_len = strlen(word.c_str());
00123                                 if (((size_t)(cmd_write - line) + name_len) < (CMD_MAXLINE - 1)) {
00124                                         strcpy(cmd_write,word.c_str());
00125                                         cmd_write += name_len;
00126                                 }
00127                                 continue;
00128                         } else {
00129                                 /* Not a command line number has to be an environment */
00130                                 char * first = strchr(cmd_read,'%');
00131                                 /* No env afterall. Ignore a single % */
00132                                 if (!first) {/* *cmd_write++ = '%';*/continue;}
00133                                 *first++ = 0;
00134                                 std::string env;
00135                                 if (shell->GetEnvStr(cmd_read,env)) {
00136                                         const char* equals = strchr(env.c_str(),'=');
00137                                         if (!equals) continue;
00138                                         equals++;
00139                                         size_t name_len = strlen(equals);
00140                                         if (((size_t)(cmd_write - line) + name_len) < (CMD_MAXLINE - 1)) {
00141                                                 strcpy(cmd_write,equals);
00142                                                 cmd_write += name_len;
00143                                         }
00144                                 }
00145                                 cmd_read = first;
00146                         }
00147                 } else {
00148                         if (((cmd_write - line) + 1) < (CMD_MAXLINE - 1))
00149                                 *cmd_write++ = *cmd_read++;
00150                 }
00151         }
00152         *cmd_write = 0;
00153         //Store current location and close bat file
00154         this->location = 0;
00155         DOS_SeekFile(file_handle,&(this->location),DOS_SEEK_CUR);
00156         DOS_CloseFile(file_handle);
00157         return true;    
00158 }
00159 
00160 bool BatchFile::Goto(const char * where) {
00161         //Open bat file and search for the where string
00162         if (!DOS_OpenFile(filename.c_str(),128,&file_handle)) {
00163                 LOG(LOG_MISC,LOG_ERROR)("SHELL:Goto Can't open BatchFile %s",filename.c_str());
00164                 delete this;
00165                 return false;
00166         }
00167 
00168         char cmd_buffer[CMD_MAXLINE];
00169         char * cmd_write;
00170 
00171         /* Scan till we have a match or return false */
00172         Bit8u c;Bit16u n;
00173 again:
00174         cmd_write=cmd_buffer;
00175         do {
00176                 n=1;
00177                 DOS_ReadFile(file_handle,&c,&n);
00178                 if (n>0) {
00179                         if (c>31) {
00180                                 if (((cmd_write - cmd_buffer) + 1) < (CMD_MAXLINE - 1))
00181                                         *cmd_write++ = (char)c;
00182                         } else {
00183                                 if (c != '\n' && c != '\r')
00184                                         shell->WriteOut(MSG_Get("SHELL_ILLEGAL_CONTROL_CHARACTER"), c, c);
00185                         }
00186                 }
00187         } while (c!='\n' && n);
00188         *cmd_write++ = 0;
00189         char *nospace = trim(cmd_buffer);
00190         if (nospace[0] == ':') {
00191                 nospace++; //Skip :
00192                 //Strip spaces and = from it.
00193                 while(*nospace && (isspace(*reinterpret_cast<unsigned char*>(nospace)) || (*nospace == '=')))
00194                         nospace++;
00195 
00196                 //label is until space/=/eol
00197                 const char* beginlabel = nospace;
00198                 while(*nospace && !isspace(*reinterpret_cast<unsigned char*>(nospace)) && (*nospace != '=')) 
00199                         nospace++;
00200 
00201                 *nospace = 0;
00202                 if (strcasecmp(beginlabel,where)==0) {
00203                 //Found it! Store location and continue
00204                         this->location = 0;
00205                         DOS_SeekFile(file_handle,&(this->location),DOS_SEEK_CUR);
00206                         DOS_CloseFile(file_handle);
00207                         return true;
00208                 }
00209            
00210         }
00211         if (!n) {
00212                 DOS_CloseFile(file_handle);
00213                 delete this;
00214                 return false;   
00215         }
00216         goto again;
00217         return false;
00218 }
00219 
00220 void BatchFile::Shift(void) {
00221         cmd->Shift(1);
00222 }