1 /*
2  *  Copyright (C) 2002-2015  The DOSBox Team
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  *
18  *  Wengier: LFN and AUTO MOUNT support
19  */
20 
21 
22 #include <stdlib.h>
23 #include <string.h>
24 #include <algorithm> //std::copy
25 #include <iterator>  //std::front_inserter
26 #include "shell.h"
27 #include "control.h"
28 #include "regs.h"
29 #include "callback.h"
30 #include "support.h"
31 
ShowPrompt(void)32 void DOS_Shell::ShowPrompt(void) {
33 	Bit8u drive=DOS_GetDefaultDrive()+'A';
34 	char dir[DOS_PATHLENGTH];
35 	dir[0] = 0; //DOS_GetCurrentDir doesn't always return something. (if drive is messed up)
36 	DOS_GetCurrentDir(0,dir,uselfn);
37 	WriteOut("%c:\\%s>",drive,dir);
38 }
39 
outc(Bit8u c)40 static void outc(Bit8u c) {
41 	Bit16u n=1;
42 	DOS_WriteFile(STDOUT,&c,&n);
43 }
44 
InputCommand(char * line)45 void DOS_Shell::InputCommand(char * line) {
46 	Bitu size=CMD_MAXLINE-2; //lastcharacter+0
47 	Bit8u c;Bit16u n=1;
48 	Bitu str_len=0;Bitu str_index=0;
49 	Bit16u len=0;
50 	bool current_hist=false; // current command stored in history?
51 
52 	line[0] = '\0';
53 
54 	std::list<std::string>::iterator it_history = l_history.begin(), it_completion = l_completion.begin();
55 
56 	while (size) {
57 		dos.echo=false;
58 		while(!DOS_ReadFile(input_handle,&c,&n)) {
59 			Bit16u dummy;
60 			DOS_CloseFile(input_handle);
61 			DOS_OpenFile("con",2,&dummy);
62 			LOG(LOG_MISC,LOG_ERROR)("Reopening the input handle. This is a bug!");
63 		}
64 		if (!n) {
65 			size=0;			//Kill the while loop
66 			continue;
67 		}
68 		switch (c) {
69 		case 0x00:				/* Extended Keys */
70 			{
71 				DOS_ReadFile(input_handle,&c,&n);
72 				switch (c) {
73 
74 				case 0x3d:		/* F3 */
75 					if (!l_history.size()) break;
76 					it_history = l_history.begin();
77 					if (it_history != l_history.end() && it_history->length() > str_len) {
78 						const char *reader = &(it_history->c_str())[str_len];
79 						while ((c = *reader++)) {
80 							line[str_index ++] = c;
81 							DOS_WriteFile(STDOUT,&c,&n);
82 						}
83 						str_len = str_index = (Bitu)it_history->length();
84 						size = CMD_MAXLINE - str_index - 2;
85 						line[str_len] = 0;
86 					}
87 					break;
88 
89 				case 0x4B:	/* LEFT */
90 					if (str_index) {
91 						outc(8);
92 						str_index --;
93 					}
94 					break;
95 
96 				case 0x4D:	/* RIGHT */
97 					if (str_index < str_len) {
98 						outc(line[str_index++]);
99 					}
100 					break;
101 
102 				case 0x47:	/* HOME */
103 					while (str_index) {
104 						outc(8);
105 						str_index--;
106 					}
107 					break;
108 
109 				case 0x4F:	/* END */
110 					while (str_index < str_len) {
111 						outc(line[str_index++]);
112 					}
113 					break;
114 
115 				case 0x48:	/* UP */
116 					if (l_history.empty() || it_history == l_history.end()) break;
117 
118 					// store current command in history if we are at beginning
119 					if (it_history == l_history.begin() && !current_hist) {
120 						current_hist=true;
121 						l_history.push_front(line);
122 					}
123 
124 					for (;str_index>0; str_index--) {
125 						// removes all characters
126 						outc(8); outc(' '); outc(8);
127 					}
128 					strcpy(line, it_history->c_str());
129 					len = (Bit16u)it_history->length();
130 					str_len = str_index = len;
131 					size = CMD_MAXLINE - str_index - 2;
132 					DOS_WriteFile(STDOUT, (Bit8u *)line, &len);
133 					it_history ++;
134 					break;
135 
136 				case 0x50:	/* DOWN */
137 					if (l_history.empty() || it_history == l_history.begin()) break;
138 
139 					// not very nice but works ..
140 					it_history --;
141 					if (it_history == l_history.begin()) {
142 						// no previous commands in history
143 						it_history ++;
144 
145 						// remove current command from history
146 						if (current_hist) {
147 							current_hist=false;
148 							l_history.pop_front();
149 						}
150 						break;
151 					} else it_history --;
152 
153 					for (;str_index>0; str_index--) {
154 						// removes all characters
155 						outc(8); outc(' '); outc(8);
156 					}
157 					strcpy(line, it_history->c_str());
158 					len = (Bit16u)it_history->length();
159 					str_len = str_index = len;
160 					size = CMD_MAXLINE - str_index - 2;
161 					DOS_WriteFile(STDOUT, (Bit8u *)line, &len);
162 					it_history ++;
163 
164 					break;
165 				case 0x53:/* DELETE */
166 					{
167 						if(str_index>=str_len) break;
168 						Bit16u a=str_len-str_index-1;
169 						Bit8u* text=reinterpret_cast<Bit8u*>(&line[str_index+1]);
170 						DOS_WriteFile(STDOUT,text,&a);//write buffer to screen
171 						outc(' ');outc(8);
172 						for(Bitu i=str_index;i<str_len-1;i++) {
173 							line[i]=line[i+1];
174 							outc(8);
175 						}
176 						line[--str_len]=0;
177 						size++;
178 					}
179 					break;
180 				case 15:		/* Shift-Tab */
181 					if (l_completion.size()) {
182 						if (it_completion == l_completion.begin()) it_completion = l_completion.end ();
183 						it_completion--;
184 
185 						if (it_completion->length()) {
186 							for (;str_index > completion_index; str_index--) {
187 								// removes all characters
188 								outc(8); outc(' '); outc(8);
189 							}
190 
191 							strcpy(&line[completion_index], it_completion->c_str());
192 							len = (Bit16u)it_completion->length();
193 							str_len = str_index = completion_index + len;
194 							size = CMD_MAXLINE - str_index - 2;
195 							DOS_WriteFile(STDOUT, (Bit8u *)it_completion->c_str(), &len);
196 						}
197 					}
198 				default:
199 					break;
200 				}
201 			};
202 			break;
203 		case 0x08:				/* BackSpace */
204 			if (str_index) {
205 				outc(8);
206 				Bit32u str_remain=str_len - str_index;
207 				size++;
208 				if (str_remain) {
209 					memmove(&line[str_index-1],&line[str_index],str_remain);
210 					line[--str_len]=0;
211 					str_index --;
212 					/* Go back to redraw */
213 					for (Bit16u i=str_index; i < str_len; i++)
214 						outc(line[i]);
215 				} else {
216 					line[--str_index] = '\0';
217 					str_len--;
218 				}
219 				outc(' ');	outc(8);
220 				// moves the cursor left
221 				while (str_remain--) outc(8);
222 			}
223 			if (l_completion.size()) l_completion.clear();
224 			break;
225 		case 0x0a:				/* New Line not handled */
226 			/* Don't care */
227 			break;
228 		case 0x0d:				/* Return */
229 			outc('\n');
230 			size=0;			//Kill the while loop
231 			break;
232 		case'\t':
233 			{
234 				if (l_completion.size()) {
235 					it_completion ++;
236 					if (it_completion == l_completion.end()) it_completion = l_completion.begin();
237 				} else {
238 					// build new completion list
239 					// Lines starting with CD will only get directories in the list
240 					bool dir_only = (strncasecmp(line,"CD ",3)==0);
241 					int q=0;
242 
243 					// get completion mask
244 					char *p_completion_start = strrchr(line, ' ');
245 					while (p_completion_start) {
246 						q=0;
247 						char *i;
248 						for (i=line;i<p_completion_start;i++)
249 							if (*i=='\"') q++;
250 						if (q/2*2==q) break;
251 						*i=0;
252 						p_completion_start = strrchr(line, ' ');
253 						*i=' ';
254 					}
255 
256 					if (p_completion_start) {
257 						p_completion_start ++;
258 						completion_index = (Bit16u)(str_len - strlen(p_completion_start));
259 					} else {
260 						p_completion_start = line;
261 						completion_index = 0;
262 					}
263 
264 					char *path;
265 					if ((path = strrchr(line+completion_index,':'))) completion_index = (Bit16u)(path-line+1);
266 					if ((path = strrchr(line+completion_index,'\\'))) completion_index = (Bit16u)(path-line+1);
267 					if ((path = strrchr(line+completion_index,'/'))) completion_index = (Bit16u)(path-line+1);
268 
269 					// build the completion list
270 					char mask[DOS_PATHLENGTH+2],smask[DOS_PATHLENGTH];
271 					if (p_completion_start) {
272 						strcpy(mask, p_completion_start);
273 						char* dot_pos=strrchr(mask,'.');
274 						char* bs_pos=strrchr(mask,'\\');
275 						char* fs_pos=strrchr(mask,'/');
276 						char* cl_pos=strrchr(mask,':');
277 						// not perfect when line already contains wildcards, but works
278 						if ((dot_pos-bs_pos>0) && (dot_pos-fs_pos>0) && (dot_pos-cl_pos>0))
279 							strcat(mask, "*");
280 						else strcat(mask, "*.*");
281 					} else {
282 						strcpy(mask, "*.*");
283 					}
284 
285 					RealPt save_dta=dos.dta();
286 					dos.dta(dos.tables.tempdta);
287 
288 					bool res = false;
289 					if (DOS_GetSFNPath(mask,smask,false)) {
290 						sprintf(mask,"\"%s\"",smask);
291 						res = DOS_FindFirst(mask, 0xffff & ~DOS_ATTR_VOLUME);
292 					}
293 					if (!res) {
294 						dos.dta(save_dta);
295 						break;	// TODO: beep
296 					}
297 
298 					DOS_DTA dta(dos.dta());
299 					char name[DOS_NAMELENGTH_ASCII], lname[LFN_NAMELENGTH], qlname[LFN_NAMELENGTH+2];
300 					Bit32u sz;Bit16u date;Bit16u time;Bit8u att;
301 
302 					std::list<std::string> executable;
303 					q=0;
304 					while (*p_completion_start)
305 						if (*p_completion_start++=='\"')
306 							q++;
307 					while (res) {
308 						dta.GetResult(name,lname,sz,date,time,att);
309 						if (strchr(uselfn?lname:name,' ')!=NULL||q/2*2!=q)
310 							sprintf(qlname,"\"%s\"",uselfn?lname:name);
311 						else
312 							strcpy(qlname,uselfn?lname:name);
313 						// add result to completion list
314 
315 						char *ext;	// file extension
316 						if (strcmp(name, ".") && strcmp(name, "..")) {
317 							if (dir_only) { //Handle the dir only case different (line starts with cd)
318 								if(att & DOS_ATTR_DIRECTORY) l_completion.push_back(qlname);
319 							} else {
320 								ext = strrchr(name, '.');
321 								if (ext && (strcmp(ext, ".BAT") == 0 || strcmp(ext, ".COM") == 0 || strcmp(ext, ".EXE") == 0))
322 									// we add executables to the a seperate list and place that list infront of the normal files
323 									executable.push_front(qlname);
324 								else
325 									l_completion.push_back(qlname);
326 							}
327 						}
328 						res=DOS_FindNext();
329 					}
330 					/* Add executable list to front of completion list. */
331 					std::copy(executable.begin(),executable.end(),std::front_inserter(l_completion));
332 					it_completion = l_completion.begin();
333 					dos.dta(save_dta);
334 				}
335 
336 				if (l_completion.size() && it_completion->length()) {
337 					for (;str_index > completion_index; str_index--) {
338 						// removes all characters
339 						outc(8); outc(' '); outc(8);
340 					}
341 
342 					strcpy(&line[completion_index], it_completion->c_str());
343 					len = (Bit16u)it_completion->length();
344 					str_len = str_index = completion_index + len;
345 					size = CMD_MAXLINE - str_index - 2;
346 					DOS_WriteFile(STDOUT, (Bit8u *)it_completion->c_str(), &len);
347 				}
348 			}
349 			break;
350 		case 0x1b:   /* ESC */
351 			//write a backslash and return to the next line
352 			outc('\\');
353 			outc('\n');
354 			*line = 0;      // reset the line.
355 			if (l_completion.size()) l_completion.clear(); //reset the completion list.
356 			this->InputCommand(line);	//Get the NEW line.
357 			size = 0;       // stop the next loop
358 			str_len = 0;    // prevent multiple adds of the same line
359 			break;
360 		default:
361 			if (l_completion.size()) l_completion.clear();
362 			if(str_index < str_len && true) { //mem_readb(BIOS_KEYBOARD_FLAGS1)&0x80) dev_con.h ?
363 				outc(' ');//move cursor one to the right.
364 				Bit16u a = str_len - str_index;
365 				Bit8u* text=reinterpret_cast<Bit8u*>(&line[str_index]);
366 				DOS_WriteFile(STDOUT,text,&a);//write buffer to screen
367 				outc(8);//undo the cursor the right.
368 				for(Bitu i=str_len;i>str_index;i--) {
369 					line[i]=line[i-1]; //move internal buffer
370 					outc(8); //move cursor back (from write buffer to screen)
371 				}
372 				line[++str_len]=0;//new end (as the internal buffer moved one place to the right
373 				size--;
374 			};
375 
376 			line[str_index]=c;
377 			str_index ++;
378 			if (str_index > str_len){
379 				line[str_index] = '\0';
380 				str_len++;
381 				size--;
382 			}
383 			DOS_WriteFile(STDOUT,&c,&n);
384 			break;
385 		}
386 	}
387 
388 	if (!str_len) return;
389 	str_len++;
390 
391 	// remove current command from history if it's there
392 	if (current_hist) {
393 		current_hist=false;
394 		l_history.pop_front();
395 	}
396 
397 	// add command line to history
398 	l_history.push_front(line); it_history = l_history.begin();
399 	if (l_completion.size()) l_completion.clear();
400 }
401 
402 std::string full_arguments = "";
Execute(char * name,char * args)403 bool DOS_Shell::Execute(char * name,char * args) {
404 /* return true  => don't check for hardware changes in do_command
405  * return false =>       check for hardware changes in do_command */
406 	char fullname[DOS_PATHLENGTH+4]; //stores results from Which
407 	char* p_fullname;
408 	char line[CMD_MAXLINE];
409 	if(strlen(args)!= 0){
410 		if(*args != ' '){ //put a space in front
411 			line[0]=' ';line[1]=0;
412 			strncat(line,args,CMD_MAXLINE-2);
413 			line[CMD_MAXLINE-1]=0;
414 		}
415 		else
416 		{
417 			safe_strncpy(line,args,CMD_MAXLINE);
418 		}
419 	}else{
420 		line[0]=0;
421 	};
422 
423 	/* check for a drive change */
424 	if (((strcmp(name + 1, ":") == 0) || (strcmp(name + 1, ":\\") == 0)) && isalpha(*name))
425 	{
426 		if (strrchr(name,'\\')) { WriteOut(MSG_Get("SHELL_EXECUTE_ILLEGAL_COMMAND"),name); return true; }
427 		if (!DOS_SetDrive(toupper(name[0])-'A')) {
428 #ifdef WIN32
429 			Section_prop * sec=0; sec=static_cast<Section_prop *>(control->GetSection("dos"));
430 			if(!sec->Get_bool("automount")) { WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_NOT_FOUND"),toupper(name[0])); return true; }
431 			// automount: attempt direct letter to drive map.
432 first_1:
433 			int drvtype=GetDriveType(name);
434 			if(drvtype==1 && strlen(name)==2 && name[1]==':') {
435 				char names[4];
436 				strcpy(names,name);
437 				strcat(names,"\\");
438 				drvtype=GetDriveType(names);
439 			}
440 			if(drvtype==2) {
441 				WriteOut(MSG_Get("SHELL_EXECUTE_AUTOMOUNT"));
442 				WriteOut("\n");
443 				WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_ACCESS_REMOVABLE"),toupper(name[0]));
444 			} else if(drvtype==4) {
445 				WriteOut(MSG_Get("SHELL_EXECUTE_AUTOMOUNT"));
446 				WriteOut("\n");
447 				WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_ACCESS_NETWORK"),toupper(name[0]));
448 			} else if(drvtype==5) {
449 				WriteOut(MSG_Get("SHELL_EXECUTE_AUTOMOUNT"));
450 				WriteOut("\n");
451 				WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_ACCESS_OPTICAL"),toupper(name[0]));
452 			} else if(drvtype==3||drvtype==6) {
453 				WriteOut(MSG_Get("SHELL_EXECUTE_AUTOMOUNT"));
454 				if(drvtype==3 && strcasecmp(name,"c:")==0)
455 					WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_ACCESS_WARNING_WIN"));
456 				WriteOut("\n");
457 				WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_ACCESS_LOCAL"),toupper(name[0]));
458 			} else {
459 				WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_NOT_FOUND"),toupper(name[0]));
460 				return true;
461 			}
462 
463 first_2:
464 		Bit8u c;Bit16u n=1;
465 		DOS_ReadFile (STDIN,&c,&n);
466 		do switch (c) {
467 			case 'n':			case 'N':
468 			{
469 				DOS_WriteFile (STDOUT,&c, &n);
470 				DOS_ReadFile (STDIN,&c,&n);
471 				do switch (c) {
472 					case 0xD: WriteOut("\n\n"); WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_NOT_FOUND"),toupper(name[0])); return true;
473 					case 0x08: WriteOut("\b \b"); goto first_2;
474 				} while (DOS_ReadFile (STDIN,&c,&n));
475 			}
476 			case 'y':			case 'Y':
477 			{
478 				DOS_WriteFile (STDOUT,&c, &n);
479 				DOS_ReadFile (STDIN,&c,&n);
480 				do switch (c) {
481 					case 0xD: WriteOut("\n"); goto continue_1;
482 					case 0x08: WriteOut("\b \b"); goto first_2;
483 				} while (DOS_ReadFile (STDIN,&c,&n));
484 			}
485 			case 0xD: WriteOut("\n"); goto first_1;
486 			case '\t': case 0x08: goto first_2;
487 			default:
488 			{
489 				DOS_WriteFile (STDOUT,&c, &n);
490 				DOS_ReadFile (STDIN,&c,&n);
491 				do switch (c) {
492 					case 0xD: WriteOut("\n");goto first_1;
493 					case 0x08: WriteOut("\b \b"); goto first_2;
494 				} while (DOS_ReadFile (STDIN,&c,&n));
495 				goto first_2;
496 			}
497 		} while (DOS_ReadFile (STDIN,&c,&n));
498 
499 continue_1:
500 
501 			char mountstring[DOS_PATHLENGTH+CROSS_LEN+20];
502 			sprintf(mountstring,"MOUNT %s ",name);
503 
504 			if(GetDriveType(name)==5) strcat(mountstring,"-t cdrom ");
505 			else if(GetDriveType(name)==2) strcat(mountstring,"-t floppy ");
506 			strcat(mountstring,name);
507 			strcat(mountstring,"\\");
508 //			if(GetDriveType(name)==5) strcat(mountstring," -ioctl");
509 
510 			this->ParseLine(mountstring);
511 			if (!DOS_SetDrive(toupper(name[0])-'A'))
512 #endif
513 			WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_NOT_FOUND"),toupper(name[0]));
514 		}
515 		return true;
516 	}
517 	/* Check for a full name */
518 	p_fullname = Which(name);
519 	if (!p_fullname) return false;
520 	strcpy(fullname,p_fullname);
521 	const char* extension = strrchr(fullname,'.');
522 
523 	/*always disallow files without extension from being executed. */
524 	/*only internal commands can be run this way and they never get in this handler */
525 	if(extension == 0)
526 	{
527 		//Check if the result will fit in the parameters. Else abort
528 		if(strlen(fullname) >( DOS_PATHLENGTH - 1) ) return false;
529 		char temp_name[DOS_PATHLENGTH+4],* temp_fullname;
530 		//try to add .com, .exe and .bat extensions to filename
531 
532 		strcpy(temp_name,fullname);
533 		strcat(temp_name,".COM");
534 		temp_fullname=Which(temp_name);
535 		if (temp_fullname) { extension=".com";strcpy(fullname,temp_fullname); }
536 
537 		else
538 		{
539 			strcpy(temp_name,fullname);
540 			strcat(temp_name,".EXE");
541 			temp_fullname=Which(temp_name);
542 		 	if (temp_fullname) { extension=".exe";strcpy(fullname,temp_fullname);}
543 
544 			else
545 			{
546 				strcpy(temp_name,fullname);
547 				strcat(temp_name,".BAT");
548 				temp_fullname=Which(temp_name);
549 		 		if (temp_fullname) { extension=".bat";strcpy(fullname,temp_fullname);}
550 
551 				else
552 				{
553 		 			return false;
554 				}
555 
556 			}
557 		}
558 	}
559 
560 	if (strcasecmp(extension, ".bat") == 0)
561 	{	/* Run the .bat file */
562 		/* delete old batch file if call is not active*/
563 		bool temp_echo=echo; /*keep the current echostate (as delete bf might change it )*/
564 		if(bf && !call) delete bf;
565 		bf=new BatchFile(this,fullname,name,line);
566 		echo=temp_echo; //restore it.
567 	}
568 	else
569 	{	/* only .bat .exe .com extensions maybe be executed by the shell */
570 		if(strcasecmp(extension, ".com") !=0)
571 		{
572 			if(strcasecmp(extension, ".exe") !=0) return false;
573 		}
574 		/* Run the .exe or .com file from the shell */
575 		/* Allocate some stack space for tables in physical memory */
576 		reg_sp-=0x200;
577 		//Add Parameter block
578 		DOS_ParamBlock block(SegPhys(ss)+reg_sp);
579 		block.Clear();
580 		//Add a filename
581 		RealPt file_name=RealMakeSeg(ss,reg_sp+0x20);
582 		MEM_BlockWrite(Real2Phys(file_name),fullname,(Bitu)(strlen(fullname)+1));
583 
584 		/* HACK: Store full commandline for mount and imgmount */
585 		full_arguments.assign(line);
586 
587 		/* Fill the command line */
588 		CommandTail cmdtail;
589 		cmdtail.count = 0;
590 		memset(&cmdtail.buffer,0,CTBUF); //Else some part of the string is unitialized (valgrind)
591 		if (strlen(line)>=CTBUF) line[CTBUF-1]=0;
592 		cmdtail.count=(Bit8u)strlen(line);
593 		memcpy(cmdtail.buffer,line,strlen(line));
594 		cmdtail.buffer[strlen(line)]=0xd;
595 		/* Copy command line in stack block too */
596 		MEM_BlockWrite(SegPhys(ss)+reg_sp+0x100,&cmdtail,CTBUF+1);
597 
598 
599 		/* Split input line up into parameters, using a few special rules, most notable the one for /AAA => A\0AA
600 		 * Qbix: It is extremly messy, but this was the only way I could get things like /:aa and :/aa to work correctly */
601 
602 		//Prepare string first
603 		char parseline[258] = { 0 };
604 		for(char *pl = line,*q = parseline; *pl ;pl++,q++) {
605 			if (*pl == '=' || *pl == ';' || *pl ==',' || *pl == '\t' || *pl == ' ') *q = 0; else *q = *pl; //Replace command seperators with 0.
606 		} //No end of string \0 needed as parseline is larger than line
607 
608 		for(char* p = parseline; (p-parseline) < 250 ;p++) { //Stay relaxed within boundaries as we have plenty of room
609 			if (*p == '/') { //Transform /Hello into H\0ello
610 				*p = 0;
611 				p++;
612 				while ( *p == 0 && (p-parseline) < 250) p++; //Skip empty fields
613 				if ((p-parseline) < 250) { //Found something. Lets get the first letter and break it up
614 					p++;
615 					memmove(static_cast<void*>(p + 1),static_cast<void*>(p),(250-(p-parseline)));
616 					if ((p-parseline) < 250) *p = 0;
617 				}
618 			}
619 		}
620 		parseline[255] = parseline[256] = parseline[257] = 0; //Just to be safe.
621 
622 		/* Parse FCB (first two parameters) and put them into the current DOS_PSP */
623 		Bit8u add;
624 		Bit16u skip = 0;
625 		//find first argument, we end up at parseline[256] if there is only one argument (similar for the second), which exists and is 0.
626 		while(skip < 256 && parseline[skip] == 0) skip++;
627 		FCB_Parsename(dos.psp(),0x5C,0x01,parseline + skip,&add);
628 		skip += add;
629 
630 		//Move to next argument if it exists
631 		while(parseline[skip] != 0) skip++;  //This is safe as there is always a 0 in parseline at the end.
632 		while(skip < 256 && parseline[skip] == 0) skip++; //Which is higher than 256
633 		FCB_Parsename(dos.psp(),0x6C,0x01,parseline + skip,&add);
634 
635 		block.exec.fcb1=RealMake(dos.psp(),0x5C);
636 		block.exec.fcb2=RealMake(dos.psp(),0x6C);
637 		/* Set the command line in the block and save it */
638 		block.exec.cmdtail=RealMakeSeg(ss,reg_sp+0x100);
639 		block.SaveData();
640 #if 0
641 		/* Save CS:IP to some point where i can return them from */
642 		Bit32u oldeip=reg_eip;
643 		Bit16u oldcs=SegValue(cs);
644 		RealPt newcsip=CALLBACK_RealPointer(call_shellstop);
645 		SegSet16(cs,RealSeg(newcsip));
646 		reg_ip=RealOff(newcsip);
647 #endif
648 		/* Start up a dos execute interrupt */
649 		reg_ax=0x4b00;
650 		//Filename pointer
651 		SegSet16(ds,SegValue(ss));
652 		reg_dx=RealOff(file_name);
653 		//Paramblock
654 		SegSet16(es,SegValue(ss));
655 		reg_bx=reg_sp;
656 		SETFLAGBIT(IF,false);
657 		CALLBACK_RunRealInt(0x21);
658 		/* Restore CS:IP and the stack */
659 		reg_sp+=0x200;
660 #if 0
661 		reg_eip=oldeip;
662 		SegSet16(cs,oldcs);
663 #endif
664 	}
665 	return true; //Executable started
666 }
667 
668 
669 
670 
671 static const char * bat_ext=".BAT";
672 static const char * com_ext=".COM";
673 static const char * exe_ext=".EXE";
674 static char which_ret[DOS_PATHLENGTH+4];
675 
Which(char * name)676 char * DOS_Shell::Which(char * name) {
677 	size_t name_len = strlen(name);
678 	if(name_len >= DOS_PATHLENGTH) return 0;
679 
680 	/* Parse through the Path to find the correct entry */
681 	/* Check if name is already ok but just misses an extension */
682 
683 	if (DOS_FileExists(name)) return name;
684 	upcase(name);
685 	if (DOS_FileExists(name)) return name;
686 	/* try to find .com .exe .bat */
687 	strcpy(which_ret,name);
688 	strcat(which_ret,com_ext);
689 	if (DOS_FileExists(which_ret)) return which_ret;
690 	strcpy(which_ret,name);
691 	strcat(which_ret,exe_ext);
692 	if (DOS_FileExists(which_ret)) return which_ret;
693 	strcpy(which_ret,name);
694 	strcat(which_ret,bat_ext);
695 	if (DOS_FileExists(which_ret)) return which_ret;
696 
697 
698 	/* No Path in filename look through path environment string */
699 	char path[DOS_PATHLENGTH];std::string temp;
700 	if (!GetEnvStr("PATH",temp)) return 0;
701 	const char * pathenv=temp.c_str();
702 	if (!pathenv) return 0;
703 	pathenv = strchr(pathenv,'=');
704 	if (!pathenv) return 0;
705 	pathenv++;
706 	Bitu i_path = 0;
707 	while (*pathenv) {
708 		/* remove ; and ;; at the beginning. (and from the second entry etc) */
709 		while(*pathenv == ';')
710 			pathenv++;
711 
712 		/* get next entry */
713 		i_path = 0; /* reset writer */
714 		while(*pathenv && (*pathenv !=';') && (i_path < DOS_PATHLENGTH) )
715 			path[i_path++] = *pathenv++;
716 
717 		if(i_path == DOS_PATHLENGTH) {
718 			/* If max size. move till next ; and terminate path */
719 			while(*pathenv && (*pathenv != ';'))
720 				pathenv++;
721 			path[DOS_PATHLENGTH - 1] = 0;
722 		} else path[i_path] = 0;
723 
724 
725 		/* check entry */
726 		if(size_t len = strlen(path)){
727 			if(len >= (DOS_PATHLENGTH - 2)) continue;
728 
729 			if(path[len - 1] != '\\') {
730 				strcat(path,"\\");
731 				len++;
732 			}
733 
734 			//If name too long =>next
735 			if((name_len + len + 1) >= DOS_PATHLENGTH) continue;
736 			strcat(path,name);
737 
738 			strcpy(which_ret,path);
739 			if (DOS_FileExists(which_ret)) return which_ret;
740 			strcpy(which_ret,path);
741 			strcat(which_ret,com_ext);
742 			if (DOS_FileExists(which_ret)) return which_ret;
743 			strcpy(which_ret,path);
744 			strcat(which_ret,exe_ext);
745 			if (DOS_FileExists(which_ret)) return which_ret;
746 			strcpy(which_ret,path);
747 			strcat(which_ret,bat_ext);
748 			if (DOS_FileExists(which_ret)) return which_ret;
749 		}
750 	}
751 	return 0;
752 }
753