DOSBox-X
|
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 }