DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/shell/shell_misc.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 #include <assert.h>
00020 #include <stdlib.h>
00021 #include <string.h>
00022 #include <algorithm> //std::copy
00023 #include <iterator>  //std::front_inserter
00024 #include "shell.h"
00025 #include "timer.h"
00026 #include "bios.h"
00027 #include "control.h"
00028 #include "regs.h"
00029 #include "callback.h"
00030 #include "support.h"
00031 #include "inout.h"
00032 #include "../ints/int10.h"
00033 #include "../dos/drives.h"
00034 #ifdef WIN32
00035 #include "../dos/cdrom.h"
00036 #endif
00037 
00038 #ifdef _MSC_VER
00039 # define MIN(a,b) ((a) < (b) ? (a) : (b))
00040 # define MAX(a,b) ((a) > (b) ? (a) : (b))
00041 #else
00042 # define MIN(a,b) std::min(a,b)
00043 # define MAX(a,b) std::max(a,b)
00044 #endif
00045 
00046 extern int lfn_filefind_handle;
00047 
00048 void DOS_Shell::ShowPrompt(void) {
00049         char dir[DOS_PATHLENGTH];
00050         dir[0] = 0; //DOS_GetCurrentDir doesn't always return something. (if drive is messed up)
00051         DOS_GetCurrentDir(0,dir,uselfn);
00052         std::string line;
00053         const char * promptstr = "\0";
00054 
00055         if(GetEnvStr("PROMPT",line)) {
00056                 std::string::size_type idx = line.find('=');
00057                 std::string value=line.substr(idx +1 , std::string::npos);
00058                 line = std::string(promptstr) + value;
00059                 promptstr = line.c_str();
00060         }
00061 
00062         while (*promptstr) {
00063                 if (!strcasecmp(promptstr,"$"))
00064                         WriteOut("\0");
00065                 else if(*promptstr != '$')
00066                         WriteOut("%c",*promptstr);
00067                 else switch (toupper(*++promptstr)) {
00068                         case 'A': WriteOut("&"); break;
00069                         case 'B': WriteOut("|"); break;
00070                         case 'C': WriteOut("("); break;
00071                         case 'D': WriteOut("%02d-%02d-%04d",dos.date.day,dos.date.month,dos.date.year); break;
00072                         case 'E': WriteOut("%c",27);  break;
00073                         case 'F': WriteOut(")");  break;
00074                         case 'G': WriteOut(">"); break;
00075                         case 'H': WriteOut("\b");   break;
00076                         case 'L': WriteOut("<"); break;
00077                         case 'N': WriteOut("%c",DOS_GetDefaultDrive()+'A'); break;
00078                         case 'P': WriteOut("%c:\\%s",DOS_GetDefaultDrive()+'A',dir); break;
00079                         case 'Q': WriteOut("="); break;
00080                         case 'S': WriteOut(" "); break;
00081                         case 'T': {
00082                                 Bitu ticks=(Bitu)(((65536.0 * 100.0)/(double)PIT_TICK_RATE)* mem_readd(BIOS_TIMER));
00083                                 reg_dl=(Bit8u)((Bitu)ticks % 100);
00084                                 ticks/=100;
00085                                 reg_dh=(Bit8u)((Bitu)ticks % 60);
00086                                 ticks/=60;
00087                                 reg_cl=(Bit8u)((Bitu)ticks % 60);
00088                                 ticks/=60;
00089                                 reg_ch=(Bit8u)((Bitu)ticks % 24);
00090                                 WriteOut("%2d:%02d:%02d.%02d",reg_ch,reg_cl,reg_dh,reg_dl);
00091                                 break;
00092                         }
00093                         case 'V': WriteOut("DOSBox version %s. Reported DOS version %d.%d.",VERSION,dos.version.major,dos.version.minor); break;
00094                         case '$': WriteOut("$"); break;
00095                         case '_': WriteOut("\n"); break;
00096                         case 'M': break;
00097                         case '+': break;
00098                 }
00099                 promptstr++;
00100         }
00101 }
00102 
00103 static void outc(Bit8u c) {
00104         Bit16u n=1;
00105         DOS_WriteFile(STDOUT,&c,&n);
00106 }
00107 
00108 static void backone() {
00109         BIOS_NCOLS;
00110         Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00111         if (CURSOR_POS_COL(page)>0)
00112                 outc(8);
00113         else if (CURSOR_POS_ROW(page)>0)
00114                 INT10_SetCursorPos(CURSOR_POS_ROW(page)-1, ncols-1, page);
00115 }
00116 
00118 void MoveCaretBackwards()
00119 {
00120         Bit8u col, row;
00121         const Bit8u page(0);
00122         INT10_GetCursorPos(&row, &col, page);
00123 
00124         if (col != 0) 
00125                 return;
00126 
00127         Bit16u cols;
00128         INT10_GetScreenColumns(&cols);
00129         INT10_SetCursorPos(row - 1, static_cast<Bit8u>(cols), page);
00130 }
00131 
00132 /* NTS: buffer pointed to by "line" must be at least CMD_MAXLINE+1 large */
00133 void DOS_Shell::InputCommand(char * line) {
00134         Bitu size=CMD_MAXLINE-2; //lastcharacter+0
00135         Bit8u c;Bit16u n=1;
00136         Bit16u str_len=0;Bit16u str_index=0;
00137         Bit16u len=0;
00138         bool current_hist=false; // current command stored in history?
00139     Bit16u cr;
00140 
00141     input_eof = false;
00142         line[0] = '\0';
00143 
00144         std::list<std::string>::iterator it_history = l_history.begin(), it_completion = l_completion.begin();
00145 
00146         while (size) {
00147                 dos.echo=false;
00148                 if (!DOS_ReadFile(input_handle,&c,&n)) {
00149             LOG(LOG_MISC,LOG_ERROR)("SHELL: Lost the input handle, dropping shell input loop");
00150             n = 0;
00151         }
00152                 if (!n) {
00153             input_eof = true;
00154                         size=0;                 //Kill the while loop
00155                         continue;
00156                 }
00157 
00158         if (input_handle != STDIN) { /* FIXME: Need DOS_IsATTY() or somesuch */
00159             cr = (Bit16u)c; /* we're not reading from the console */
00160         }
00161         else if (IS_PC98_ARCH) {
00162             extern Bit16u last_int16_code;
00163 
00164             /* shift state is needed for some key combinations not directly supported by CON driver.
00165              * bit 4 = CTRL
00166              * bit 3 = GRPH/ALT
00167              * bit 2 = kana
00168              * bit 1 = caps
00169              * bit 0 = SHIFT */
00170             uint8_t shiftstate = mem_readb(0x52A + 0x0E);
00171 
00172             /* NTS: PC-98 keyboards lack the US layout HOME / END keys, therefore there is no mapping here */
00173 
00174             /* NTS: Since left arrow and backspace map to the same byte value, PC-98 treats it the same at the DOS prompt.
00175              *      However the PC-98 version of DOSKEY seems to be able to differentiate the two anyway and let the left
00176              *      arrow move the cursor back (perhaps it's calling INT 18h directly then?) */
00177                  if (c == 0x0B)
00178                 cr = 0x4800;    /* IBM extended code up arrow */
00179             else if (c == 0x0A)
00180                 cr = 0x5000;    /* IBM extended code down arrow */
00181                  else if (c == 0x0C) {
00182                      if (shiftstate & 0x10/*CTRL*/)
00183                          cr = 0x7400;    /* IBM extended code CTRL + right arrow */
00184                      else
00185                          cr = 0x4D00;    /* IBM extended code right arrow */
00186                  }
00187             else if (c == 0x08) {
00188                 /* IBM extended code left arrow OR backspace. use last scancode to tell which as DOSKEY apparently can. */
00189                 if (last_int16_code == 0x3B00) {
00190                     if (shiftstate & 0x10/*CTRL*/)
00191                         cr = 0x7300; /* CTRL + left arrow */
00192                     else
00193                         cr = 0x4B00; /* left arrow */
00194                 }
00195                 else {
00196                     cr = 0x08; /* backspace */
00197                 }
00198             }
00199             else if (c == 0x1B) { /* escape */
00200                 /* Either it really IS the ESC key, or an ANSI code */
00201                 if (last_int16_code != 0x001B) {
00202                     DOS_ReadFile(input_handle,&c,&n);
00203                          if (c == 0x44)  // DEL
00204                         cr = 0x5300;
00205                     else if (c == 0x50)  // INS
00206                         cr = 0x5200;
00207                     else if (c == 0x53)  // F1
00208                         cr = 0x3B00;
00209                     else if (c == 0x54)  // F2
00210                         cr = 0x3C00;
00211                     else if (c == 0x55)  // F3
00212                         cr = 0x3D00;
00213                     else if (c == 0x56)  // F4
00214                         cr = 0x3E00;
00215                     else if (c == 0x57)  // F5
00216                         cr = 0x3F00;
00217                     else if (c == 0x45)  // F6
00218                         cr = 0x4000;
00219                     else if (c == 0x4A)  // F7
00220                         cr = 0x4100;
00221                     else if (c == 0x51)  // F9
00222                         cr = 0x4300;
00223                     else if (c == 0x5A)  // F10
00224                         cr = 0x4400;
00225                     else
00226                         cr = 0;
00227                 }
00228                 else {
00229                     cr = (Bit16u)c;
00230                 }
00231             }
00232             else {
00233                 cr = (Bit16u)c;
00234             }
00235         }
00236         else {
00237             if (c == 0) {
00238                                 DOS_ReadFile(input_handle,&c,&n);
00239                 cr = (Bit16u)c << (Bit16u)8;
00240             }
00241             else {
00242                 cr = (Bit16u)c;
00243             }
00244         }
00245 
00246         switch (cr) {
00247             case 0x3d00:        /* F3 */
00248                 if (!l_history.size()) break;
00249                 it_history = l_history.begin();
00250                 if (it_history != l_history.end() && it_history->length() > str_len) {
00251                     const char *reader = &(it_history->c_str())[str_len];
00252                     while ((c = (Bit8u)(*reader++))) {
00253                         line[str_index ++] = (char)c;
00254                         DOS_WriteFile(STDOUT,&c,&n);
00255                     }
00256                     str_len = str_index = (Bit16u)it_history->length();
00257                     size = (unsigned int)CMD_MAXLINE - str_index - 2u;
00258                     line[str_len] = 0;
00259                 }
00260                 break;
00261 
00262             case 0x4B00:        /* LEFT */
00263                 if (str_index) {
00264                     backone();
00265                     str_index --;
00266                         MoveCaretBackwards();
00267                 }
00268                 break;
00269 
00270                         case 0x7400: /*CTRL + RIGHT : cmd.exe-like next word*/
00271                                 {
00272                                         auto pos = line + str_index;
00273                                         auto spc = *pos == ' ';
00274                                         const auto end = line + str_len;
00275 
00276                                         while (pos < end) {
00277                                                 if (spc && *pos != ' ')
00278                                                         break;
00279                                                 if (*pos == ' ')
00280                                                         spc = true;
00281                                                 pos++;
00282                                         }
00283                                         
00284                                         const auto lgt = MIN(pos, end) - (line + str_index);
00285                                         
00286                                         for (auto i = 0; i < lgt; i++)
00287                                                 outc(static_cast<Bit8u>(line[str_index++]));
00288                                 }       
00289                         break;
00290                         case 0x7300: /*CTRL + LEFT : cmd.exe-like previous word*/
00291                                 {
00292                                         auto pos = line + str_index - 1;
00293                                         const auto beg = line;
00294                                         const auto spc = *pos == ' ';
00295 
00296                                         if (spc) {
00297                                                 while(*pos == ' ') pos--;
00298                                                 while(*pos != ' ') pos--;
00299                                                 pos++;
00300                                         }
00301                                         else {
00302                                                 while(*pos != ' ') pos--;
00303                                                 pos++;
00304                                         }
00305                                         
00306                                         const auto lgt = std::abs(MAX(pos, beg) - (line + str_index));
00307                                         
00308                                         for (auto i = 0; i < lgt; i++) {
00309                                                 backone();
00310                                                 str_index--;
00311                                                 MoveCaretBackwards();
00312                                         }
00313                                 }       
00314                         break;
00315             case 0x4D00:        /* RIGHT */
00316                 if (str_index < str_len) {
00317                     outc((Bit8u)line[str_index++]);
00318                 }
00319                 break;
00320 
00321             case 0x4700:        /* HOME */
00322                 while (str_index) {
00323                     backone();
00324                     str_index--;
00325                 }
00326                 break;
00327 
00328             case 0x5200:    /* INS */
00329                 if (IS_PC98_ARCH) { // INS state handled by IBM PC/AT BIOS, faked for PC-98 mode
00330                     extern bool pc98_doskey_insertmode;
00331 
00332                     // NTS: No visible change to the cursor, just like DOSKEY on PC-98 MS-DOS
00333                     pc98_doskey_insertmode = !pc98_doskey_insertmode;
00334                 }
00335                 break;
00336 
00337             case 0x4F00:        /* END */
00338                 while (str_index < str_len) {
00339                     outc((Bit8u)line[str_index++]);
00340                 }
00341                 break;
00342 
00343             case 0x4800:        /* UP */
00344                 if (l_history.empty() || it_history == l_history.end()) break;
00345 
00346                 // store current command in history if we are at beginning
00347                 if (it_history == l_history.begin() && !current_hist) {
00348                     current_hist=true;
00349                     l_history.push_front(line);
00350                 }
00351 
00352                 // ensure we're at end to handle all cases
00353                 while (str_index < str_len) {
00354                     outc((Bit8u)line[str_index++]);
00355                 }
00356 
00357                 for (;str_index>0; str_index--) {
00358                     // removes all characters
00359                     backone(); outc(' '); backone();
00360                 }
00361                 strcpy(line, it_history->c_str());
00362                 len = (Bit16u)it_history->length();
00363                 str_len = str_index = len;
00364                 size = (unsigned int)CMD_MAXLINE - str_index - 2u;
00365                 DOS_WriteFile(STDOUT, (Bit8u *)line, &len);
00366                 ++it_history;
00367                 break;
00368 
00369             case 0x5000:        /* DOWN */
00370                 if (l_history.empty() || it_history == l_history.begin()) break;
00371 
00372                 // not very nice but works ..
00373                 --it_history;
00374                 if (it_history == l_history.begin()) {
00375                     // no previous commands in history
00376                     ++it_history;
00377 
00378                     // remove current command from history
00379                     if (current_hist) {
00380                         current_hist=false;
00381                         l_history.pop_front();
00382                     }
00383                     break;
00384                 } else --it_history;
00385 
00386                 // ensure we're at end to handle all cases
00387                 while (str_index < str_len) {
00388                     outc((Bit8u)line[str_index++]);
00389                 }
00390 
00391                 for (;str_index>0; str_index--) {
00392                     // removes all characters
00393                     backone(); outc(' '); backone();
00394                 }
00395                 strcpy(line, it_history->c_str());
00396                 len = (Bit16u)it_history->length();
00397                 str_len = str_index = len;
00398                 size = (unsigned int)CMD_MAXLINE - str_index - 2u;
00399                 DOS_WriteFile(STDOUT, (Bit8u *)line, &len);
00400                 ++it_history;
00401 
00402                 break;
00403             case 0x5300:/* DELETE */
00404                 {
00405                     if(str_index>=str_len) break;
00406                     Bit16u a=str_len-str_index-1;
00407                     Bit8u* text=reinterpret_cast<Bit8u*>(&line[str_index+1]);
00408                     DOS_WriteFile(STDOUT,text,&a);//write buffer to screen
00409                     outc(' ');backone();
00410                     for(Bitu i=str_index;i<(str_len-1u);i++) {
00411                         line[i]=line[i+1u];
00412                         backone();
00413                     }
00414                     line[--str_len]=0;
00415                     size++;
00416                 }
00417                 break;
00418             case 0x0F00:        /* Shift-Tab */
00419                 if (l_completion.size()) {
00420                     if (it_completion == l_completion.begin()) it_completion = l_completion.end (); 
00421                     --it_completion;
00422 
00423                     if (it_completion->length()) {
00424                         for (;str_index > completion_index; str_index--) {
00425                             // removes all characters
00426                             backone(); outc(' '); backone();
00427                         }
00428 
00429                         strcpy(&line[completion_index], it_completion->c_str());
00430                         len = (Bit16u)it_completion->length();
00431                         str_len = str_index = (Bitu)(completion_index + len);
00432                         size = (unsigned int)CMD_MAXLINE - str_index - 2u;
00433                         DOS_WriteFile(STDOUT, (Bit8u *)it_completion->c_str(), &len);
00434                     }
00435                 }
00436                 break;
00437             case 0x08:                          /* BackSpace */
00438                 if (str_index) {
00439                     backone();
00440                     Bit32u str_remain=(Bit32u)(str_len - str_index);
00441                     size++;
00442                     if (str_remain) {
00443                         memmove(&line[str_index-1],&line[str_index],str_remain);
00444                         line[--str_len]=0;
00445                         str_index --;
00446                         /* Go back to redraw */
00447                         for (Bit16u i=str_index; i < str_len; i++)
00448                             outc((Bit8u)line[i]);
00449                     } else {
00450                         line[--str_index] = '\0';
00451                         str_len--;
00452                     }
00453                     outc(' ');  backone();
00454                     // moves the cursor left
00455                     while (str_remain--) backone();
00456                 }
00457                 if (l_completion.size()) l_completion.clear();
00458                 break;
00459             case 0x0a:                          /* Give a new Line */
00460                 outc('\n');
00461                 break;
00462             case '': // FAKE CTRL-C
00463                 outc(94); outc('C');
00464                 *line = 0;      // reset the line.
00465                 if (l_completion.size()) l_completion.clear(); //reset the completion list.
00466                 if(!echo) outc('\n');
00467                 size = 0;       // stop the next loop
00468                 str_len = 0;    // prevent multiple adds of the same line
00469                 break;
00470             case 0x0d:                          /* Don't care, and return */
00471                 if(!echo) { outc('\r'); outc('\n'); }
00472                 size=0;                 //Kill the while loop
00473                 break;
00474             case'\t':
00475                 {
00476                     if (l_completion.size()) {
00477                         ++it_completion;
00478                         if (it_completion == l_completion.end()) it_completion = l_completion.begin();
00479                     } else {
00480                         // build new completion list
00481                         // Lines starting with CD/MD/RD will only get directories in the list
00482                                                 bool dir_only = (strncasecmp(line,"CD ",3)==0)||(strncasecmp(line,"MD ",3)==0)||(strncasecmp(line,"RD ",3)==0)||
00483                                                                 (strncasecmp(line,"CHDIR ",6)==0)||(strncasecmp(line,"MKDIR ",3)==0)||(strncasecmp(line,"RMDIR ",6)==0);
00484                                                 int q=0, r=0, k=0;
00485 
00486                         // get completion mask
00487                         const char *p_completion_start = strrchr(line, ' ');
00488                                                 while (p_completion_start) {
00489                                 q=0;
00490                                 char *i;
00491                                 for (i=line;i<p_completion_start;i++)
00492                                    if (*i=='\"') q++;
00493                                 if (q/2*2==q) break;
00494                                 *i=0;
00495                                 p_completion_start = strrchr(line, ' ');
00496                                 *i=' ';
00497                             }
00498                                                 char c[]={'<','>','|'};
00499                                                 for (unsigned int j=0; j<sizeof(c); j++) {
00500                                                         const char *sp = strrchr(line, c[j]);
00501                                                         while (sp) {
00502                                                                 q=0;
00503                                                                 char *i;
00504                                                                 for (i=line;i<sp;i++)
00505                                                                         if (*i=='\"') q++;
00506                                                                 if (q/2*2==q) break;
00507                                                                 *i=0;
00508                                                                 sp = strrchr(line, c[j]);
00509                                                                 *i=c[j];
00510                                                         }
00511                                                         if (!p_completion_start || p_completion_start<sp)
00512                                                                 p_completion_start = sp;
00513                                                 }
00514 
00515                         if (p_completion_start) {
00516                             p_completion_start ++;
00517                             completion_index = (Bit16u)(str_len - strlen(p_completion_start));
00518                         } else {
00519                             p_completion_start = line;
00520                             completion_index = 0;
00521                         }
00522                                                 k=completion_index;
00523 
00524                         const char *path;
00525                                                 if ((path = strrchr(line+completion_index,':'))) completion_index = (Bit16u)(path-line+1);
00526                         if ((path = strrchr(line+completion_index,'\\'))) completion_index = (Bit16u)(path-line+1);
00527                         if ((path = strrchr(line+completion_index,'/'))) completion_index = (Bit16u)(path-line+1);
00528 
00529                         // build the completion list
00530                         char mask[DOS_PATHLENGTH+2] = {0}, smask[DOS_PATHLENGTH] = {0};
00531                         if (p_completion_start && strlen(p_completion_start) + 3 >= DOS_PATHLENGTH) {
00532                                                         // TODO: This really should be done in the CON driver so that this code can just print ASCII code 7 instead
00533                                                         if (IS_PC98_ARCH) {
00534                                                                 // TODO: BEEP. I/O PORTS ARE DIFFERENT AS IS THE PIT CLOCK RATE
00535                                                         }
00536                                                         else {
00537                                                                 // IBM PC/XT/AT
00538                                                                 IO_Write(0x43,0xb6);
00539                                                                 IO_Write(0x42,1750&0xff);
00540                                                                 IO_Write(0x42,1750>>8);
00541                                                                 IO_Write(0x61,IO_Read(0x61)|0x3);
00542                                                                 for(Bitu i=0; i < 333; i++) CALLBACK_Idle();
00543                                                                 IO_Write(0x61,IO_Read(0x61)&~0x3);
00544                                                         }
00545                             break;
00546                         }
00547                         if (p_completion_start) {
00548                             safe_strncpy(mask, p_completion_start,DOS_PATHLENGTH);
00549                             const char* dot_pos = strrchr(mask, '.');
00550                             const char* bs_pos = strrchr(mask, '\\');
00551                             const char* fs_pos = strrchr(mask, '/');
00552                             const char* cl_pos = strrchr(mask, ':');
00553                             // not perfect when line already contains wildcards, but works
00554                             if ((dot_pos-bs_pos>0) && (dot_pos-fs_pos>0) && (dot_pos-cl_pos>0))
00555                                 strncat(mask, "*",DOS_PATHLENGTH - 1);
00556                             else strncat(mask, "*.*",DOS_PATHLENGTH - 1);
00557                         } else {
00558                             strcpy(mask, "*.*");
00559                         }
00560 
00561                         RealPt save_dta=dos.dta();
00562                         dos.dta(dos.tables.tempdta);
00563                                                 
00564                                                 bool res = false;
00565                                                 if (DOS_GetSFNPath(mask,smask,false)) {
00566                                                         sprintf(mask,"\"%s\"",smask);
00567                                                         int fbak=lfn_filefind_handle;
00568                                                         lfn_filefind_handle=uselfn?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE;
00569                                                         res = DOS_FindFirst(mask, 0xffff & ~DOS_ATTR_VOLUME);
00570                                                         lfn_filefind_handle=fbak;
00571                                                 }
00572                         if (!res) {
00573                             dos.dta(save_dta);
00574                                                         // TODO: This really should be done in the CON driver so that this code can just print ASCII code 7 instead
00575                                                         if (IS_PC98_ARCH) {
00576                                                                 // TODO: BEEP. I/O PORTS ARE DIFFERENT AS IS THE PIT CLOCK RATE
00577                                                         }
00578                                                         else {
00579                                                                 // IBM PC/XT/AT
00580                                                                 IO_Write(0x43,0xb6);
00581                                                                 IO_Write(0x42,1750&0xff);
00582                                                                 IO_Write(0x42,1750>>8);
00583                                                                 IO_Write(0x61,IO_Read(0x61)|0x3);
00584                                                                 for(Bitu i=0; i < 300; i++) CALLBACK_Idle();
00585                                                                 IO_Write(0x61,IO_Read(0x61)&~0x3);
00586                                                         }
00587                             break;
00588                         }
00589 
00590                         DOS_DTA dta(dos.dta());
00591                                                 char name[DOS_NAMELENGTH_ASCII], lname[LFN_NAMELENGTH], qlname[LFN_NAMELENGTH+2];
00592                         Bit32u sz;Bit16u date;Bit16u time;Bit8u att;
00593 
00594                         std::list<std::string> executable;
00595                                                 q=0;r=0;
00596                                                 while (*p_completion_start) {
00597                                                         k++;
00598                                                         if (*p_completion_start++=='\"') {
00599                                                                 if (k<=completion_index)
00600                                                                         q++;
00601                                                                 else
00602                                                                         r++;
00603                                                         }
00604                                                 }
00605                                                 int fbak=lfn_filefind_handle;
00606                                                 lfn_filefind_handle=uselfn?LFN_FILEFIND_INTERNAL:LFN_FILEFIND_NONE;
00607                         while (res) {
00608                                                         dta.GetResult(name,lname,sz,date,time,att);
00609                                                         if ((strchr(uselfn?lname:name,' ')!=NULL&&q/2*2==q)||r)
00610                                                                 sprintf(qlname,q/2*2!=q?"%s\"":"\"%s\"",uselfn?lname:name);
00611                                                         else
00612                                 strcpy(qlname,uselfn?lname:name);
00613                             // add result to completion list
00614 
00615                             if (strcmp(name, ".") && strcmp(name, "..")) {
00616                                 if (dir_only) { //Handle the dir only case different (line starts with cd)
00617                                                                         if(att & DOS_ATTR_DIRECTORY) l_completion.push_back(qlname);
00618                                 } else {
00619                                     const char *ext = strrchr(name, '.'); // file extension
00620                                     if (ext && (strcmp(ext, ".BAT") == 0 || strcmp(ext, ".COM") == 0 || strcmp(ext, ".EXE") == 0))
00621                                         // we add executables to the a seperate list and place that list infront of the normal files
00622                                         executable.push_front(qlname);
00623                                     else
00624                                                                                 l_completion.push_back(qlname);
00625                                 }
00626                             }
00627                             res=DOS_FindNext();
00628                         }
00629                                                 lfn_filefind_handle=fbak;
00630                         /* Add executable list to front of completion list. */
00631                         std::copy(executable.begin(),executable.end(),std::front_inserter(l_completion));
00632                         it_completion = l_completion.begin();
00633                         dos.dta(save_dta);
00634                     }
00635 
00636                     if (l_completion.size() && it_completion->length()) {
00637                         for (;str_index > completion_index; str_index--) {
00638                             // removes all characters
00639                             backone(); outc(' '); backone();
00640                         }
00641 
00642                         strcpy(&line[completion_index], it_completion->c_str());
00643                         len = (Bit16u)it_completion->length();
00644                         str_len = str_index = (Bitu)(completion_index + len);
00645                         size = (unsigned int)CMD_MAXLINE - str_index - 2u;
00646                         DOS_WriteFile(STDOUT, (Bit8u *)it_completion->c_str(), &len);
00647                     }
00648                 }
00649                 break;
00650             case 0x1b:   /* ESC */
00651                 // NTS: According to real PC-98 DOS:
00652                 //      If DOSKEY is loaded, ESC clears the prompt
00653                 //      If DOSKEY is NOT loaded, ESC does nothing. In fact, after ESC,
00654                 //      the next character input is thrown away before resuming normal keyboard input.
00655                 //
00656                 //      DOSBox / DOSBox-X have always acted as if DOSKEY is loaded in a fashion, so
00657                 //      we'll emulate the PC-98 DOSKEY behavior here.
00658                 //
00659                 //      DOSKEY on PC-98 is able to clear the whole prompt and even bring the cursor
00660                 //      back up to the first line if the input crosses multiple lines.
00661 
00662                 // NTS: According to real IBM/Microsoft PC/AT DOS:
00663                 //      If DOSKEY is loaded, ESC clears the prompt
00664                 //      If DOSKEY is NOT loaded, ESC prints a backslash and goes to the next line.
00665                 //      The Windows 95 version of DOSKEY puts the cursor at a horizontal position
00666                 //      that matches the DOS prompt (not emulated here).
00667                 //
00668                 //      DOSBox / DOSBox-X have always acted as if DOSKEY is loaded in a fashion, so
00669                 //      we'll emulate DOSKEY behavior here.
00670                 while (str_index < str_len) {
00671                     outc(' ');
00672                     str_index++;
00673                 }
00674                 while (str_index > 0) {
00675                     backone();
00676                     outc(' ');
00677                     backone();
00678                     MoveCaretBackwards();
00679                     str_index--;
00680                 }
00681 
00682                 *line = 0;      // reset the line.
00683                 if (l_completion.size()) l_completion.clear(); //reset the completion list.
00684                 str_index = 0;
00685                 str_len = 0;
00686                 break;
00687             default:
00688                 if (cr >= 0x100) break;
00689                 if (l_completion.size()) l_completion.clear();
00690                 if(str_index < str_len && !INT10_GetInsertState()) { //mem_readb(BIOS_KEYBOARD_FLAGS1)&0x80) dev_con.h ?
00691                     outc(' ');//move cursor one to the right.
00692                     Bit16u a = str_len - str_index;
00693                     Bit8u* text=reinterpret_cast<Bit8u*>(&line[str_index]);
00694                     DOS_WriteFile(STDOUT,text,&a);//write buffer to screen
00695                     backone();//undo the cursor the right.
00696                     for(Bitu i=str_len;i>str_index;i--) {
00697                         line[i]=line[i-1]; //move internal buffer
00698                         backone(); //move cursor back (from write buffer to screen)
00699                     }
00700                     line[++str_len]=0;//new end (as the internal buffer moved one place to the right
00701                     size--;
00702                 }
00703 
00704                 line[str_index]=(char)(cr&0xFF);
00705                 str_index ++;
00706                 if (str_index > str_len){ 
00707                     line[str_index] = '\0';
00708                     str_len++;
00709                     size--;
00710                 }
00711                 DOS_WriteFile(STDOUT,&c,&n);
00712                 break;
00713         }
00714     }
00715 
00716         if (!str_len) return;
00717         str_len++;
00718 
00719         // remove current command from history if it's there
00720         if (current_hist) {
00721                 current_hist=false;
00722                 l_history.pop_front();
00723         }
00724 
00725         // add command line to history. Win95 behavior with DOSKey suggests
00726         // that the original string is preserved, not the expanded string.
00727         l_history.push_front(line); it_history = l_history.begin();
00728         if (l_completion.size()) l_completion.clear();
00729 
00730         /* DOS %variable% substitution */
00731         ProcessCmdLineEnvVarStitution(line);
00732 }
00733 
00734 
00735 /* WARNING: Substitution is carried out in-place!
00736  * Buffer pointed to by "line" must be at least CMD_MAXLINE+1 bytes long! */
00737 void DOS_Shell::ProcessCmdLineEnvVarStitution(char *line) {
00738         char temp[CMD_MAXLINE]; /* <- NTS: Currently 4096 which is very generous indeed! */
00739     char* w = temp;
00740     const char* wf = temp + sizeof(temp) - 1;
00741         char *r=line;
00742 
00743         /* initial scan: is there anything to substitute? */
00744         /* if not, then return without modifying "line" */
00745         while (*r != 0 && *r != '%') r++;
00746         if (*r != '%') return;
00747 
00748         /* if the incoming string is already too long, then that's a problem too! */
00749         if (((size_t)(r+1-line)) >= CMD_MAXLINE) {
00750                 LOG_MSG("DOS_Shell::ProcessCmdLineEnvVarStitution WARNING incoming string to substitute is already too long!\n");
00751                 goto overflow;
00752         }
00753 
00754         /* copy the string down up to that point */
00755     for (const char* c = line; c < r;) {
00756                 assert(w < wf);
00757                 *w++ = *c++;
00758         }
00759 
00760         /* begin substitution process */
00761         while (*r != 0) {
00762                 if (*r == '%') {
00763                         r++;
00764                         if (*r == '%' || *r == 0) {
00765                                 /* %% or leaving a trailing % at the end (Win95 behavior) becomes a single '%' */
00766                                 if (w >= wf) goto overflow;
00767                                 *w++ = '%';
00768                                 if (*r != 0) r++;
00769                                 else break;
00770                         }
00771                         else {
00772                 const char* name = r; /* store pointer, 'r' is first char of the name following '%' */
00773                                 int spaces = 0,chars = 0;
00774 
00775                                 /* continue scanning for the ending '%'. variable names are apparently meant to be
00776                                  * alphanumeric, start with a letter, without spaces (if Windows 95 COMMAND.COM is
00777                                  * any good example). If it doesn't end in '%' or is broken by space or starts with
00778                                  * a number, substitution is not carried out. In the middle of the variable name
00779                                  * it seems to be acceptable to use hyphens.
00780                                  *
00781                                  * since spaces break up a variable name to prevent substitution, these commands
00782                                  * act differently from one another:
00783                                  *
00784                                  * C:>echo %PATH%
00785                                  * C:\DOS;C:\WINDOWS
00786                                  *
00787                                  * C:>echo %PATH %
00788                                  * %PATH % */
00789                                 if (isalpha(*r) || *r == ' ') { /* must start with a letter. space is apparently valid too. (Win95) */
00790                                         if (*r == ' ') spaces++;
00791                                         else if (isalpha(*r)) chars++;
00792 
00793                                         r++;
00794                                         while (*r != 0 && *r != '%') {
00795                                                 if (*r == ' ') spaces++;
00796                                                 else chars++;
00797                                                 r++;
00798                                         }
00799                                 }
00800 
00801                                 /* Win95 testing:
00802                                  *
00803                                  * "%" = "%"
00804                                  * "%%" = "%"
00805                                  * "% %" = ""
00806                                  * "%  %" = ""
00807                                  * "% % %" = " %"
00808                                  *
00809                                  * ^ WTF?
00810                                  *
00811                                  * So the below code has funny conditions to match Win95's weird rules on what
00812                                  * consitutes valid or invalid %variable% names. */
00813                                 if (*r == '%' && ((spaces > 0 && chars == 0) || (spaces == 0 && chars > 0))) {
00814                                         std::string temp2;
00815 
00816                                         /* valid name found. substitute */
00817                                         *r++ = 0; /* ASCIIZ snip */
00818                                         if (GetEnvStr(name,temp2)) {
00819                                                 size_t equ_pos = temp2.find_first_of('=');
00820                                                 if (equ_pos != std::string::npos) {
00821                                                         const char *base = temp2.c_str();
00822                                                         const char *value = base + equ_pos + 1;
00823                                                         const char *fence = base + temp2.length();
00824                                                         assert(value >= base && value <= fence);
00825                                                         size_t len = (size_t)(fence-value);
00826 
00827                                                         if ((w+len) > wf) goto overflow;
00828                                                         memcpy(w,value,len);
00829                                                         w += len;
00830                                                 }
00831                                         }
00832                                 }
00833                                 else {
00834                                         /* nope. didn't find a valid name */
00835 
00836                                         while (*r == ' ') r++; /* skip spaces */
00837                                         name--; /* step "name" back to cover the first '%' we found */
00838 
00839                     for (const char* c = name; c < r;) {
00840                                                 if (w >= wf) goto overflow;
00841                                                 *w++ = *c++;
00842                                         }
00843                                 }
00844                         }
00845                 }
00846                 else {
00847                         if (w >= wf) goto overflow;
00848                         *w++ = *r++;
00849                 }
00850         }
00851 
00852         /* complete the C-string */
00853         assert(w <= wf);
00854         *w = 0;
00855 
00856         /* copy the string back over the buffer pointed to by line */
00857         {
00858                 size_t out_len = (size_t)(w+1-temp); /* length counting the NUL too */
00859                 assert(out_len <= CMD_MAXLINE);
00860                 memcpy(line,temp,out_len);
00861         }
00862 
00863         /* success */
00864         return;
00865 overflow:
00866         *line = 0; /* clear string (C-string chop with NUL) */
00867         WriteOut("Command input error: string expansion overflow\n");
00868 }
00869 
00870 std::string full_arguments = "";
00871 bool infix=false;
00872 extern bool packerr;
00873 bool DOS_Shell::Execute(char* name, const char* args) {
00874 /* return true  => don't check for hardware changes in do_command 
00875  * return false =>       check for hardware changes in do_command */
00876         char fullname[DOS_PATHLENGTH+4]; //stores results from Which
00877     const char* p_fullname;
00878         char line[CMD_MAXLINE];
00879         if(strlen(args)!= 0){
00880                 if(*args != ' '){ //put a space in front
00881                         line[0]=' ';line[1]=0;
00882                         strncat(line,args,CMD_MAXLINE-2);
00883                         line[CMD_MAXLINE-1]=0;
00884                 }
00885                 else
00886                 {
00887                         safe_strncpy(line,args,CMD_MAXLINE);
00888                 }
00889         }else{
00890                 line[0]=0;
00891         }
00892 
00893         /* check for a drive change */
00894         if (((strcmp(name + 1, ":") == 0) || (strcmp(name + 1, ":\\") == 0)) && isalpha(*name))
00895         {
00896                 if (strrchr(name,'\\')) { WriteOut(MSG_Get("SHELL_EXECUTE_ILLEGAL_COMMAND"),name); return true; }
00897                 if (!DOS_SetDrive(toupper(name[0])-'A')) {
00898 #ifdef WIN32
00899             const Section_prop* sec = 0; sec = static_cast<Section_prop*>(control->GetSection("dos"));
00900                         if(!sec->Get_bool("automount")) { WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_NOT_FOUND"),toupper(name[0])); return true; }
00901                         // automount: attempt direct letter to drive map.
00902                         int type=GetDriveType(name);
00903                         if(type==DRIVE_FIXED && (strcasecmp(name,"C:")==0)) WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_ACCESS_WARNING_WIN"));
00904 first_1:
00905                         if(type==DRIVE_CDROM) WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_ACCESS_CDROM"),toupper(name[0]));
00906                         else if(type==DRIVE_REMOVABLE && (strcasecmp(name,"A:")==0||strcasecmp(name,"B:")==0)) WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_ACCESS_FLOPPY"),toupper(name[0]));
00907                         else if(type==DRIVE_REMOVABLE) WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_ACCESS_REMOVABLE"),toupper(name[0]));
00908                         else if(type==DRIVE_REMOTE) WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_ACCESS_NETWORK"),toupper(name[0]));
00909                         else if((type==DRIVE_FIXED)||(type==DRIVE_RAMDISK)) WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_ACCESS_FIXED"),toupper(name[0]));
00910                         else { WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_NOT_FOUND"),toupper(name[0])); return true; }
00911 
00912 first_2:
00913                 Bit8u c;Bit16u n=1;
00914                 DOS_ReadFile (STDIN,&c,&n);
00915                 do switch (c) {
00916                         case 'n':                       case 'N':
00917                         {
00918                                 DOS_WriteFile (STDOUT,&c, &n);
00919                                 DOS_ReadFile (STDIN,&c,&n);
00920                                 do switch (c) {
00921                                         case 0xD: WriteOut("\n\n"); WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_NOT_FOUND"),toupper(name[0])); return true;
00922                                         case 0x3: WriteOut("^C\n");return true;
00923                                         case 0x8: WriteOut("\b \b"); goto first_2;
00924                                 } while (DOS_ReadFile (STDIN,&c,&n));
00925                         }
00926                         case 'y':                       case 'Y':
00927                         {
00928                                 DOS_WriteFile (STDOUT,&c, &n);
00929                                 DOS_ReadFile (STDIN,&c,&n);
00930                                 do switch (c) {
00931                                         case 0xD: WriteOut("\n"); goto continue_1;
00932                                         case 0x3: WriteOut("^C\n");return true;
00933                                         case 0x8: WriteOut("\b \b"); goto first_2;
00934                                 } while (DOS_ReadFile (STDIN,&c,&n));
00935                         }
00936                         case 0x3: WriteOut("^C\n");return true;
00937                         case 0xD: WriteOut("\n"); goto first_1;
00938                         case '\t': case 0x08: goto first_2;
00939                         default:
00940                         {
00941                                 DOS_WriteFile (STDOUT,&c, &n);
00942                                 DOS_ReadFile (STDIN,&c,&n);
00943                                 do switch (c) {
00944                                         case 0xD: WriteOut("\n");goto first_1;
00945                                         case 0x3: WriteOut("^C\n");return true;
00946                                         case 0x8: WriteOut("\b \b"); goto first_2;
00947                                 } while (DOS_ReadFile (STDIN,&c,&n));
00948                                 goto first_2;
00949                         }
00950                 } while (DOS_ReadFile (STDIN,&c,&n));
00951 
00952 continue_1:
00953 
00954                         char mountstring[DOS_PATHLENGTH+CROSS_LEN+20];
00955                         sprintf(mountstring,"MOUNT %s ",name);
00956 
00957                         if(type==DRIVE_CDROM) strcat(mountstring,"-t cdrom ");
00958                         else if(type==DRIVE_REMOVABLE && (strcasecmp(name,"A:")==0||strcasecmp(name,"B:")==0)) strcat(mountstring,"-t floppy ");
00959                         strcat(mountstring,name);
00960                         strcat(mountstring,"\\");
00961 //                      if(GetDriveType(name)==5) strcat(mountstring," -ioctl");
00962                         
00963                         this->ParseLine(mountstring);
00964 //failed:
00965                         if (!DOS_SetDrive(toupper(name[0])-'A'))
00966 #endif
00967                         WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_NOT_FOUND"),toupper(name[0]));
00968                 }
00969                 return true;
00970         }
00971         /* Check for a full name */
00972         p_fullname = Which(name);
00973         if (!p_fullname) return false;
00974         strcpy(fullname,p_fullname);
00975         const char* extension = strrchr(fullname,'.');
00976         
00977         /*always disallow files without extension from being executed. */
00978         /*only internal commands can be run this way and they never get in this handler */
00979         if(extension == 0)
00980         {
00981                 //Check if the result will fit in the parameters. Else abort
00982                 if(strlen(fullname) >( DOS_PATHLENGTH - 1) ) return false;
00983         char temp_name[DOS_PATHLENGTH + 4];
00984         const char* temp_fullname;
00985                 //try to add .com, .exe and .bat extensions to filename
00986                 
00987                 strcpy(temp_name,fullname);
00988                 strcat(temp_name,".COM");
00989                 temp_fullname=Which(temp_name);
00990                 if (temp_fullname) { extension=".com";strcpy(fullname,temp_fullname); }
00991 
00992                 else 
00993                 {
00994                         strcpy(temp_name,fullname);
00995                         strcat(temp_name,".EXE");
00996                         temp_fullname=Which(temp_name);
00997                         if (temp_fullname) { extension=".exe";strcpy(fullname,temp_fullname);}
00998 
00999                         else 
01000                         {
01001                                 strcpy(temp_name,fullname);
01002                                 strcat(temp_name,".BAT");
01003                                 temp_fullname=Which(temp_name);
01004                                 if (temp_fullname) { extension=".bat";strcpy(fullname,temp_fullname);}
01005 
01006                                 else  
01007                                 {
01008                                         return false;
01009                                 }
01010                         
01011                         }       
01012                 }
01013         }
01014         
01015         if (strcasecmp(extension, ".bat") == 0) 
01016         {       /* Run the .bat file */
01017                 /* delete old batch file if call is not active*/
01018                 bool temp_echo=echo; /*keep the current echostate (as delete bf might change it )*/
01019                 if(bf && !call) delete bf;
01020                 bf=new BatchFile(this,fullname,name,line);
01021                 echo=temp_echo; //restore it.
01022         } 
01023         else 
01024         {       /* only .bat .exe .com extensions maybe be executed by the shell */
01025                 if(strcasecmp(extension, ".com") !=0) 
01026                 {
01027                         if(strcasecmp(extension, ".exe") !=0) return false;
01028                 }
01029                 /* Run the .exe or .com file from the shell */
01030                 /* Allocate some stack space for tables in physical memory */
01031                 reg_sp-=0x200;
01032                 //Add Parameter block
01033                 DOS_ParamBlock block(SegPhys(ss)+reg_sp);
01034                 block.Clear();
01035                 //Add a filename
01036                 RealPt file_name=RealMakeSeg(ss,reg_sp+0x20);
01037                 MEM_BlockWrite(Real2Phys(file_name),fullname,(Bitu)(strlen(fullname)+1));
01038 
01039                 /* HACK: Store full commandline for mount and imgmount */
01040                 full_arguments.assign(line);
01041 
01042                 /* Fill the command line */
01043                 CommandTail cmdtail;
01044                 cmdtail.count = 0;
01045         memset(&cmdtail.buffer,0,CTBUF); //Else some part of the string is unitialized (valgrind)
01046         if (strlen(line)>=CTBUF) line[CTBUF-1]=0;
01047                 cmdtail.count=(Bit8u)strlen(line);
01048                 memcpy(cmdtail.buffer,line,strlen(line));
01049                 cmdtail.buffer[strlen(line)]=0xd;
01050                 /* Copy command line in stack block too */
01051                 MEM_BlockWrite(SegPhys(ss)+reg_sp+0x100,&cmdtail,CTBUF+1);
01052                 
01053                 /* Split input line up into parameters, using a few special rules, most notable the one for /AAA => A\0AA
01054                  * Qbix: It is extremly messy, but this was the only way I could get things like /:aa and :/aa to work correctly */
01055                 
01056                 //Prepare string first
01057                 char parseline[258] = { 0 };
01058                 for(char *pl = line,*q = parseline; *pl ;pl++,q++) {
01059                         if (*pl == '=' || *pl == ';' || *pl ==',' || *pl == '\t' || *pl == ' ') *q = 0; else *q = *pl; //Replace command seperators with 0.
01060                 } //No end of string \0 needed as parseline is larger than line
01061 
01062                 for(char* p = parseline; (p-parseline) < 250 ;p++) { //Stay relaxed within boundaries as we have plenty of room
01063                         if (*p == '/') { //Transform /Hello into H\0ello
01064                                 *p = 0;
01065                                 p++;
01066                                 while ( *p == 0 && (p-parseline) < 250) p++; //Skip empty fields
01067                                 if ((p-parseline) < 250) { //Found something. Lets get the first letter and break it up
01068                                         p++;
01069                                         memmove(static_cast<void*>(p + 1),static_cast<void*>(p),(250u-(unsigned int)(p-parseline)));
01070                                         if ((p-parseline) < 250) *p = 0;
01071                                 }
01072                         }
01073                 }
01074                 parseline[255] = parseline[256] = parseline[257] = 0; //Just to be safe.
01075 
01076                 /* Parse FCB (first two parameters) and put them into the current DOS_PSP */
01077                 Bit8u add;
01078                 Bit16u skip = 0;
01079                 //find first argument, we end up at parseline[256] if there is only one argument (similar for the second), which exists and is 0.
01080                 while(skip < 256 && parseline[skip] == 0) skip++;
01081                 FCB_Parsename(dos.psp(),0x5C,0x01,parseline + skip,&add);
01082                 skip += add;
01083                 
01084                 //Move to next argument if it exists
01085                 while(parseline[skip] != 0) skip++;  //This is safe as there is always a 0 in parseline at the end.
01086                 while(skip < 256 && parseline[skip] == 0) skip++; //Which is higher than 256
01087                 FCB_Parsename(dos.psp(),0x6C,0x01,parseline + skip,&add);
01088 
01089                 block.exec.fcb1=RealMake(dos.psp(),0x5C);
01090                 block.exec.fcb2=RealMake(dos.psp(),0x6C);
01091                 /* Set the command line in the block and save it */
01092                 block.exec.cmdtail=RealMakeSeg(ss,reg_sp+0x100);
01093                 block.SaveData();
01094 #if 0
01095                 /* Save CS:IP to some point where i can return them from */
01096                 Bit32u oldeip=reg_eip;
01097                 Bit16u oldcs=SegValue(cs);
01098                 RealPt newcsip=CALLBACK_RealPointer(call_shellstop);
01099                 SegSet16(cs,RealSeg(newcsip));
01100                 reg_ip=RealOff(newcsip);
01101 #endif
01102                 packerr=false;
01103                 /* Start up a dos execute interrupt */
01104                 reg_ax=0x4b00;
01105                 //Filename pointer
01106                 SegSet16(ds,SegValue(ss));
01107                 reg_dx=RealOff(file_name);
01108                 //Paramblock
01109                 SegSet16(es,SegValue(ss));
01110                 reg_bx=reg_sp;
01111                 SETFLAGBIT(IF,false);
01112                 CALLBACK_RunRealInt(0x21);
01113                 /* Restore CS:IP and the stack */
01114                 reg_sp+=0x200;
01115 #if 0
01116                 reg_eip=oldeip;
01117                 SegSet16(cs,oldcs);
01118 #endif
01119                 if (packerr&&!infix) {
01120                         Bit16u segment;
01121                         Bit16u blocks = (Bit16u)(64*1024/16);
01122                         if (DOS_AllocateMemory(&segment,&blocks)) {
01123                                 DOS_MCB mcb((Bit16u)(segment-1));
01124                                 mcb.SetPSPSeg(0x40);
01125                                 WriteOut("\r\nTrying to run with LOADFIX..\r\n");
01126                                 infix=true;
01127                                 Execute(name, args);
01128                                 infix=false;
01129                                 DOS_FreeMemory(segment);
01130                         }
01131                 }
01132                 packerr=false;
01133         }
01134         return true; //Executable started
01135 }
01136 
01137 
01138 
01139 
01140 static const char * bat_ext=".BAT";
01141 static const char * com_ext=".COM";
01142 static const char * exe_ext=".EXE";
01143 static char which_ret[DOS_PATHLENGTH+4], s_ret[DOS_PATHLENGTH+4];
01144 
01145 char * DOS_Shell::Which(char * name) {
01146         size_t name_len = strlen(name);
01147         if(name_len >= DOS_PATHLENGTH) return 0;
01148 
01149         /* Parse through the Path to find the correct entry */
01150         /* Check if name is already ok but just misses an extension */
01151 
01152         if (DOS_FileExists(name)) return name;
01153         upcase(name);
01154         if (DOS_FileExists(name)) return name;
01155         /* try to find .com .exe .bat */
01156         strcpy(which_ret,name);
01157         strcat(which_ret,com_ext);
01158         if (DOS_FileExists(which_ret)) return which_ret;
01159         strcpy(which_ret,name);
01160         strcat(which_ret,exe_ext);
01161         if (DOS_FileExists(which_ret)) return which_ret;
01162         strcpy(which_ret,name);
01163         strcat(which_ret,bat_ext);
01164         if (DOS_FileExists(which_ret)) return which_ret;
01165 
01166 
01167         /* No Path in filename look through path environment string */
01168         char path[DOS_PATHLENGTH];std::string temp;
01169         if (!GetEnvStr("PATH",temp)) return 0;
01170         const char * pathenv=temp.c_str();
01171         if (!pathenv) return 0;
01172         pathenv = strchr(pathenv,'=');
01173         if (!pathenv) return 0;
01174         pathenv++;
01175         while (*pathenv) {
01176                 /* remove ; and ;; at the beginning. (and from the second entry etc) */
01177                 while(*pathenv == ';')
01178                         pathenv++;
01179 
01180                 /* get next entry */
01181                 Bitu i_path = 0; /* reset writer */
01182                 while(*pathenv && (*pathenv !=';') && (i_path < DOS_PATHLENGTH) )
01183                         path[i_path++] = *pathenv++;
01184 
01185                 if(i_path == DOS_PATHLENGTH) {
01186                         /* If max size. move till next ; and terminate path */
01187                         while(*pathenv && (*pathenv != ';')) 
01188                                 pathenv++;
01189                         path[DOS_PATHLENGTH - 1] = 0;
01190                 } else path[i_path] = 0;
01191 
01192                 int k=0;
01193                 for (int i=0;i<(int)strlen(path);i++)
01194                         if (path[i]!='\"')
01195                                 path[k++]=path[i];
01196                 path[k]=0;
01197 
01198                 /* check entry */
01199                 if(size_t len = strlen(path)){
01200                         if(len >= (DOS_PATHLENGTH - 2)) continue;
01201 
01202                         if (uselfn&&len>3) {
01203                                 if (path[len - 1]=='\\') path[len - 1]=0;
01204                                 if (DOS_GetSFNPath(("\""+std::string(path)+"\"").c_str(), s_ret, false))
01205                                         strcpy(path, s_ret);
01206                                 len = strlen(path);
01207                         }
01208 
01209                         if(path[len - 1] != '\\') {
01210                                 strcat(path,"\\"); 
01211                                 len++;
01212                         }
01213 
01214                         //If name too long =>next
01215                         if((name_len + len + 1) >= DOS_PATHLENGTH) continue;
01216                         strcat(path,strchr(name, ' ')?("\""+std::string(name)+"\"").c_str():name);
01217 
01218                         strcpy(which_ret,path);
01219                         if (DOS_FileExists(which_ret)) return strchr(which_ret, '\"')&&DOS_GetSFNPath(which_ret, s_ret, false)?s_ret:which_ret;
01220                         strcpy(which_ret,path);
01221                         strcat(which_ret,com_ext);
01222                         if (DOS_FileExists(which_ret)) return strchr(which_ret, '\"')&&DOS_GetSFNPath(which_ret, s_ret, false)?s_ret:which_ret;
01223                         strcpy(which_ret,path);
01224                         strcat(which_ret,exe_ext);
01225                         if (DOS_FileExists(which_ret)) return strchr(which_ret, '\"')&&DOS_GetSFNPath(which_ret, s_ret, false)?s_ret:which_ret;
01226                         strcpy(which_ret,path);
01227                         strcat(which_ret,bat_ext);
01228                         if (DOS_FileExists(which_ret)) return strchr(which_ret, '\"')&&DOS_GetSFNPath(which_ret, s_ret, false)?s_ret:which_ret;
01229                 }
01230         }
01231         return 0;
01232 }