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 support
19 */
20
21
22 #include "dosbox.h"
23 #include "shell.h"
24 #include "callback.h"
25 #include "regs.h"
26 #include "bios.h"
27 #include "../dos/drives.h"
28 #include "support.h"
29 #include "control.h"
30 #include <cstring>
31 #include <cctype>
32 #include <cstdlib>
33 #include <vector>
34 #include <string>
35 #include <time.h>
36 #include <stdlib.h>
37
38 static SHELL_Cmd cmd_list[]={
39 { "DIR", 0, &DOS_Shell::CMD_DIR, "SHELL_CMD_DIR_HELP"},
40 { "LS", 0, &DOS_Shell::CMD_DIR, "SHELL_CMD_DIR_HELP"},
41 { "CHDIR", 1, &DOS_Shell::CMD_CHDIR, "SHELL_CMD_CHDIR_HELP"},
42 { "ATTRIB", 1, &DOS_Shell::CMD_ATTRIB, "SHELL_CMD_ATTRIB_HELP"},
43 { "CALL", 1, &DOS_Shell::CMD_CALL, "SHELL_CMD_CALL_HELP"},
44 { "CD", 0, &DOS_Shell::CMD_CHDIR, "SHELL_CMD_CHDIR_HELP"},
45 { "CHOICE", 1, &DOS_Shell::CMD_CHOICE, "SHELL_CMD_CHOICE_HELP"},
46 { "CLS", 0, &DOS_Shell::CMD_CLS, "SHELL_CMD_CLS_HELP"},
47 { "COPY", 0, &DOS_Shell::CMD_COPY, "SHELL_CMD_COPY_HELP"},
48 { "DATE", 0, &DOS_Shell::CMD_DATE, "SHELL_CMD_DATE_HELP"},
49 { "DEL", 0, &DOS_Shell::CMD_DELETE, "SHELL_CMD_DELETE_HELP"},
50 { "DELETE", 1, &DOS_Shell::CMD_DELETE, "SHELL_CMD_DELETE_HELP"},
51 { "ERASE", 1, &DOS_Shell::CMD_DELETE, "SHELL_CMD_DELETE_HELP"},
52 { "ECHO", 1, &DOS_Shell::CMD_ECHO, "SHELL_CMD_ECHO_HELP"},
53 { "EXIT", 0, &DOS_Shell::CMD_EXIT, "SHELL_CMD_EXIT_HELP"},
54 { "GOTO", 1, &DOS_Shell::CMD_GOTO, "SHELL_CMD_GOTO_HELP"},
55 { "HELP", 1, &DOS_Shell::CMD_HELP, "SHELL_CMD_HELP_HELP"},
56 { "IF", 1, &DOS_Shell::CMD_IF, "SHELL_CMD_IF_HELP"},
57 { "LOADHIGH", 1, &DOS_Shell::CMD_LOADHIGH, "SHELL_CMD_LOADHIGH_HELP"},
58 { "LH", 1, &DOS_Shell::CMD_LOADHIGH, "SHELL_CMD_LOADHIGH_HELP"},
59 { "MKDIR", 1, &DOS_Shell::CMD_MKDIR, "SHELL_CMD_MKDIR_HELP"},
60 { "MD", 0, &DOS_Shell::CMD_MKDIR, "SHELL_CMD_MKDIR_HELP"},
61 { "PATH", 1, &DOS_Shell::CMD_PATH, "SHELL_CMD_PATH_HELP"},
62 { "PAUSE", 1, &DOS_Shell::CMD_PAUSE, "SHELL_CMD_PAUSE_HELP"},
63 { "RMDIR", 1, &DOS_Shell::CMD_RMDIR, "SHELL_CMD_RMDIR_HELP"},
64 { "RD", 0, &DOS_Shell::CMD_RMDIR, "SHELL_CMD_RMDIR_HELP"},
65 { "REM", 1, &DOS_Shell::CMD_REM, "SHELL_CMD_REM_HELP"},
66 { "RENAME", 1, &DOS_Shell::CMD_RENAME, "SHELL_CMD_RENAME_HELP"},
67 { "REN", 0, &DOS_Shell::CMD_RENAME, "SHELL_CMD_RENAME_HELP"},
68 { "SET", 1, &DOS_Shell::CMD_SET, "SHELL_CMD_SET_HELP"},
69 { "SHIFT", 1, &DOS_Shell::CMD_SHIFT, "SHELL_CMD_SHIFT_HELP"},
70 { "SUBST", 1, &DOS_Shell::CMD_SUBST, "SHELL_CMD_SUBST_HELP"},
71 { "TIME", 0, &DOS_Shell::CMD_TIME, "SHELL_CMD_TIME_HELP"},
72 { "TYPE", 0, &DOS_Shell::CMD_TYPE, "SHELL_CMD_TYPE_HELP"},
73 { "VER", 0, &DOS_Shell::CMD_VER, "SHELL_CMD_VER_HELP"},
74 {0,0,0,0}
75 };
76
77 /* support functions */
78 static char empty_char = 0;
79 static char* empty_string = &empty_char;
StripSpaces(char * & args)80 static void StripSpaces(char*&args) {
81 while(args && *args && isspace(*reinterpret_cast<unsigned char*>(args)))
82 args++;
83 }
84
StripSpaces(char * & args,char also)85 static void StripSpaces(char*&args,char also) {
86 while(args && *args && (isspace(*reinterpret_cast<unsigned char*>(args)) || (*args == also)))
87 args++;
88 }
89
ExpandDot(char * args,char * buffer)90 static char* ExpandDot(char*args, char* buffer) {
91 if(*args == '.') {
92 if(*(args+1) == 0){
93 strcpy(buffer,"*.*");
94 return buffer;
95 }
96 if( (*(args+1) != '.') && (*(args+1) != '\\') ) {
97 buffer[0] = '*';
98 buffer[1] = 0;
99 strcat(buffer,args);
100 return buffer;
101 } else
102 strcpy (buffer, args);
103 }
104 else strcpy(buffer,args);
105 return buffer;
106 }
107
108
109
CheckConfig(char * cmd_in,char * line)110 bool DOS_Shell::CheckConfig(char* cmd_in,char*line) {
111 Section* test = control->GetSectionFromProperty(cmd_in);
112 if(!test) return false;
113 if(line && !line[0]) {
114 std::string val = test->GetPropValue(cmd_in);
115 if(val != NO_SUCH_PROPERTY) WriteOut("%s\n",val.c_str());
116 return true;
117 }
118 char newcom[1024]; newcom[0] = 0; strcpy(newcom,"z:\\config -set ");
119 strcat(newcom,test->GetName()); strcat(newcom," ");
120 strcat(newcom,cmd_in);strcat(newcom,line);
121 DoCommand(newcom);
122 return true;
123 }
124
DoCommand(char * line)125 void DOS_Shell::DoCommand(char * line) {
126 /* First split the line into command and arguments */
127 line=trim(line);
128 char cmd_buffer[CMD_MAXLINE];
129 char * cmd_write=cmd_buffer;
130 while (*line) {
131 if (*line == 32) break;
132 if (*line == '/') break;
133 if (*line == '\t') break;
134 if (*line == '=') break;
135 // if (*line == ':') break; //This breaks drive switching as that is handled at a later stage.
136 if ((*line == '.') ||(*line == '\\')) { //allow stuff like cd.. and dir.exe cd\kees
137 *cmd_write=0;
138 Bit32u cmd_index=0;
139 while (cmd_list[cmd_index].name) {
140 if (strcasecmp(cmd_list[cmd_index].name,cmd_buffer)==0) {
141 (this->*(cmd_list[cmd_index].handler))(line);
142 return;
143 }
144 cmd_index++;
145 }
146 }
147 *cmd_write++=*line++;
148 }
149 *cmd_write=0;
150 if (strlen(cmd_buffer)==0) return;
151 /* Check the internal list */
152 Bit32u cmd_index=0;
153 while (cmd_list[cmd_index].name) {
154 if (strcasecmp(cmd_list[cmd_index].name,cmd_buffer)==0) {
155 (this->*(cmd_list[cmd_index].handler))(line);
156 return;
157 }
158 cmd_index++;
159 }
160 /* This isn't an internal command execute it */
161 if(Execute(cmd_buffer,line)) return;
162 if(CheckConfig(cmd_buffer,line)) return;
163 WriteOut(MSG_Get("SHELL_EXECUTE_ILLEGAL_COMMAND"),cmd_buffer);
164 }
165
166 #define HELP(command) \
167 if (ScanCMDBool(args,"?")) { \
168 WriteOut(MSG_Get("SHELL_CMD_" command "_HELP")); \
169 const char* long_m = MSG_Get("SHELL_CMD_" command "_HELP_LONG"); \
170 WriteOut("\n"); \
171 if(strcmp("Message not Found!\n",long_m)) WriteOut(long_m); \
172 else WriteOut(command "\n"); \
173 return; \
174 }
175
CMD_CLS(char * args)176 void DOS_Shell::CMD_CLS(char * args) {
177 HELP("CLS");
178 reg_ax=0x0003;
179 CALLBACK_RunRealInt(0x10);
180 }
181
CMD_DELETE(char * args)182 void DOS_Shell::CMD_DELETE(char * args) {
183 HELP("DELETE");
184 /* Command uses dta so set it to our internal dta */
185 RealPt save_dta=dos.dta();
186 dos.dta(dos.tables.tempdta);
187
188 char * rem=ScanCMDRemain(args);
189 if (rem) {
190 WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
191 return;
192 }
193 /* If delete accept switches mind the space infront of them. See the dir /p code */
194
195 char full[DOS_PATHLENGTH],sfull[DOS_PATHLENGTH+2];
196 char buffer[CROSS_LEN];
197 args = ExpandDot(args,buffer);
198 StripSpaces(args);
199 if (!DOS_Canonicalize(args,full)) { WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));return; }
200 //TODO Maybe support confirmation for *.* like dos does.
201 char spath[DOS_PATHLENGTH],sargs[DOS_PATHLENGTH];
202 if (!DOS_GetSFNPath(args,spath,false)) {
203 WriteOut(MSG_Get("SHELL_CMD_DEL_ERROR"),args);
204 return;
205 }
206 sprintf(sargs,"\"%s\"",spath);
207 bool res=DOS_FindFirst(sargs,0xffff & ~DOS_ATTR_VOLUME);
208 if (!res) {
209 WriteOut(MSG_Get("SHELL_CMD_DEL_ERROR"),args);
210 dos.dta(save_dta);
211 return;
212 }
213 //end can't be 0, but if it is we'll get a nice crash, who cares :)
214 char * end=strrchr(full,'\\')+1;*end=0;
215 char name[DOS_NAMELENGTH_ASCII],lname[LFN_NAMELENGTH+1];
216 Bit32u size;Bit16u time,date;Bit8u attr;
217 DOS_DTA dta(dos.dta());
218 while (res) {
219 dta.GetResult(name,lname,size,date,time,attr);
220 if (!(attr & (DOS_ATTR_DIRECTORY|DOS_ATTR_READ_ONLY))) {
221 strcpy(end,name);
222 strcpy(sfull,full);
223 if (uselfn) sprintf(sfull,"\"%s\"",full);
224 if (!DOS_UnlinkFile(sfull)) WriteOut(MSG_Get("SHELL_CMD_DEL_ERROR"),full);
225 }
226 res=DOS_FindNext();
227 }
228 dos.dta(save_dta);
229 }
230
CMD_HELP(char * args)231 void DOS_Shell::CMD_HELP(char * args){
232 HELP("HELP");
233 bool optall=ScanCMDBool(args,"ALL");
234 /* Print the help */
235 if(!optall) WriteOut(MSG_Get("SHELL_CMD_HELP"));
236 Bit32u cmd_index=0,write_count=0;
237 while (cmd_list[cmd_index].name) {
238 if (optall || !cmd_list[cmd_index].flags) {
239 WriteOut("<\033[34;1m%-8s\033[0m> %s",cmd_list[cmd_index].name,MSG_Get(cmd_list[cmd_index].help));
240 if(!(++write_count%22)) CMD_PAUSE(empty_string);
241 }
242 cmd_index++;
243 }
244 }
245
CMD_RENAME(char * args)246 void DOS_Shell::CMD_RENAME(char * args){
247 HELP("RENAME");
248 StripSpaces(args);
249 if (!*args) {SyntaxError();return;}
250 if ((strchr(args,'*')!=NULL) || (strchr(args,'?')!=NULL) ) { WriteOut(MSG_Get("SHELL_CMD_NO_WILD"));return;}
251 char * arg1=StripArg(args);
252 StripSpaces(args);
253 if (!*args) {SyntaxError();return;}
254 char* slash = strrchr(arg1,'\\');
255 if (slash) {
256 /* If directory specified (crystal caves installer)
257 * rename from c:\X : rename c:\abc.exe abc.shr.
258 * File must appear in C:\
259 * Ren X:\A\B C => ren X:\A\B X:\A\C */
260
261 char dir_source[DOS_PATHLENGTH + 4] = {0}; //not sure if drive portion is included in pathlength
262 //Copy first and then modify, makes GCC happy
263 safe_strncpy(dir_source,arg1,DOS_PATHLENGTH + 4);
264 char* dummy = strrchr(dir_source,'\\');
265 if (!dummy) { //Possible due to length
266 WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
267 return;
268 }
269 dummy++;
270 *dummy = 0;
271
272 //Maybe check args for directory, as I think that isn't allowed
273
274 //dir_source and target are introduced for when we support multiple files being renamed.
275 char target[DOS_PATHLENGTH+CROSS_LEN + 5] = {0};
276 strcpy(target,dir_source);
277 strncat(target,args,CROSS_LEN);
278
279 DOS_Rename(arg1,target);
280
281 } else {
282 DOS_Rename(arg1,args);
283 }
284 }
285
CMD_ECHO(char * args)286 void DOS_Shell::CMD_ECHO(char * args){
287 if (!*args) {
288 if (echo) { WriteOut(MSG_Get("SHELL_CMD_ECHO_ON"));}
289 else { WriteOut(MSG_Get("SHELL_CMD_ECHO_OFF"));}
290 return;
291 }
292 char buffer[512];
293 char* pbuffer = buffer;
294 safe_strncpy(buffer,args,512);
295 StripSpaces(pbuffer);
296 if (strcasecmp(pbuffer,"OFF")==0) {
297 echo=false;
298 return;
299 }
300 if (strcasecmp(pbuffer,"ON")==0) {
301 echo=true;
302 return;
303 }
304 if(strcasecmp(pbuffer,"/?")==0) { HELP("ECHO"); }
305
306 args++;//skip first character. either a slash or dot or space
307 size_t len = strlen(args); //TODO check input of else ook nodig is.
308 if(len && args[len - 1] == '\r') {
309 LOG(LOG_MISC,LOG_WARN)("Hu ? carriage return already present. Is this possible?");
310 WriteOut("%s\n",args);
311 } else WriteOut("%s\r\n",args);
312 }
313
314
CMD_EXIT(char * args)315 void DOS_Shell::CMD_EXIT(char * args) {
316 HELP("EXIT");
317 exit = true;
318 }
319
CMD_CHDIR(char * args)320 void DOS_Shell::CMD_CHDIR(char * args) {
321 HELP("CHDIR");
322 StripSpaces(args);
323 char sargs[CROSS_LEN];
324 if (*args && !DOS_GetSFNPath(args,sargs,false)) {
325 WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
326 return;
327 }
328 Bit8u drive = DOS_GetDefaultDrive()+'A';
329 char dir[DOS_PATHLENGTH];
330 if (!*args) {
331 DOS_GetCurrentDir(0,dir,true);
332 WriteOut("%c:\\%s\n",drive,dir);
333 } else if(strlen(args) == 2 && args[1]==':') {
334 Bit8u targetdrive = (args[0] | 0x20)-'a' + 1;
335 unsigned char targetdisplay = *reinterpret_cast<unsigned char*>(&args[0]);
336 if(!DOS_GetCurrentDir(targetdrive,dir,true)) {
337 if(drive == 'Z') {
338 WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_NOT_FOUND"),toupper(targetdisplay));
339 } else {
340 WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
341 }
342 return;
343 }
344 WriteOut("%c:\\%s\n",toupper(targetdisplay),dir);
345 if(drive == 'Z')
346 WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT"),toupper(targetdisplay));
347 } else if (!DOS_ChangeDir(sargs)) {
348 /* Changedir failed. Check if the filename is longer then 8 and/or contains spaces */
349
350 std::string temps(args),slashpart;
351 std::string::size_type separator = temps.find_first_of("\\/");
352 if(!separator) {
353 slashpart = temps.substr(0,1);
354 temps.erase(0,1);
355 }
356 separator = temps.find_first_of("\\/");
357 if(separator != std::string::npos) temps.erase(separator);
358 separator = temps.find_first_of("\"");
359 if(separator != std::string::npos) temps.erase(separator);
360 separator = temps.rfind('.');
361 if(separator != std::string::npos) temps.erase(separator);
362 separator = temps.find(' ');
363 if(separator != std::string::npos) {/* Contains spaces */
364 temps.erase(separator);
365 if(temps.size() >6) temps.erase(6);
366 temps += "~1";
367 WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT_2"),temps.insert(0,slashpart).c_str());
368 } else {
369 if (drive == 'Z') {
370 WriteOut(MSG_Get("SHELL_CMD_CHDIR_HINT_3"));
371 } else {
372 WriteOut(MSG_Get("SHELL_CMD_CHDIR_ERROR"),args);
373 }
374 }
375 }
376 }
377
CMD_MKDIR(char * args)378 void DOS_Shell::CMD_MKDIR(char * args) {
379 HELP("MKDIR");
380 StripSpaces(args);
381 char * rem=ScanCMDRemain(args);
382 if (rem) {
383 WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
384 return;
385 }
386 if (!DOS_MakeDir(args)) {
387 WriteOut(MSG_Get("SHELL_CMD_MKDIR_ERROR"),args);
388 }
389 }
390
CMD_RMDIR(char * args)391 void DOS_Shell::CMD_RMDIR(char * args) {
392 HELP("RMDIR");
393 StripSpaces(args);
394 char * rem=ScanCMDRemain(args);
395 if (rem) {
396 WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
397 return;
398 }
399 if (!DOS_RemoveDir(args)) {
400 WriteOut(MSG_Get("SHELL_CMD_RMDIR_ERROR"),args);
401 }
402 }
403
FormatNumber(Bit32u num,char * buf)404 static void FormatNumber(Bit32u num,char * buf) {
405 Bit32u numm,numk,numb,numg;
406 numb=num % 1000;
407 num/=1000;
408 numk=num % 1000;
409 num/=1000;
410 numm=num % 1000;
411 num/=1000;
412 numg=num;
413 if (numg) {
414 sprintf(buf,"%d,%03d,%03d,%03d",numg,numm,numk,numb);
415 return;
416 };
417 if (numm) {
418 sprintf(buf,"%d,%03d,%03d",numm,numk,numb);
419 return;
420 };
421 if (numk) {
422 sprintf(buf,"%d,%03d",numk,numb);
423 return;
424 };
425 sprintf(buf,"%d",numb);
426 }
427
CMD_DIR(char * args)428 void DOS_Shell::CMD_DIR(char * args) {
429 HELP("DIR");
430 char numformat[16];
431 char path[DOS_PATHLENGTH];
432 char sargs[CROSS_LEN];
433
434 std::string line;
435 if(GetEnvStr("DIRCMD",line)){
436 std::string::size_type idx = line.find('=');
437 std::string value=line.substr(idx +1 , std::string::npos);
438 line = std::string(args) + " " + value;
439 args=const_cast<char*>(line.c_str());
440 }
441
442 bool optW=ScanCMDBool(args,"W");
443 ScanCMDBool(args,"S");
444 bool optP=ScanCMDBool(args,"P");
445 if (ScanCMDBool(args,"WP") || ScanCMDBool(args,"PW")) {
446 optW=optP=true;
447 }
448 bool optB=ScanCMDBool(args,"B");
449 bool optAD=ScanCMDBool(args,"AD");
450 char * rem=ScanCMDRemain(args);
451 if (rem) {
452 WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
453 return;
454 }
455 Bit32u byte_count,file_count,dir_count;
456 Bitu w_count=0;
457 Bitu p_count=0;
458 Bitu w_size = optW?5:1;
459 byte_count=file_count=dir_count=0;
460
461 char buffer[CROSS_LEN];
462 args = trim(args);
463 size_t argLen = strlen(args);
464 if (argLen == 0) {
465 strcpy(args,"*.*"); //no arguments.
466 } else {
467 switch (args[argLen-1])
468 {
469 case '\\': // handle \, C:\, etc.
470 case ':' : // handle C:, etc.
471 strcat(args,"*.*");
472 break;
473 default:
474 break;
475 }
476 }
477 args = ExpandDot(args,buffer);
478
479 if (!strrchr(args,'*') && !strrchr(args,'?')) {
480 Bit16u attribute=0;
481 if(!DOS_GetSFNPath(args,sargs,false)) {
482 WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
483 return;
484 }
485 if(DOS_GetFileAttr(sargs,&attribute) && (attribute&DOS_ATTR_DIRECTORY) ) {
486 DOS_FindFirst(sargs,0xffff & ~DOS_ATTR_VOLUME);
487 DOS_DTA dta(dos.dta());
488 strcpy(args,sargs);
489 strcat(args,"\\*.*"); // if no wildcard and a directory, get its files
490 }
491 }
492 if (!DOS_GetSFNPath(args,sargs,false)) {
493 WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
494 return;
495 }
496 sprintf(args,"\"%s\"",sargs);
497 if (!strrchr(args,'.')) {
498 strcat(args,".*"); // if no extension, get them all
499 }
500
501 /* Make a full path in the args */
502 if (!DOS_Canonicalize(args,path)) {
503 WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
504 return;
505 }
506 *(strrchr(path,'\\')+1)=0;
507 if (!DOS_GetSFNPath(path,sargs,true)) {
508 WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
509 return;
510 }
511 if (*(sargs+strlen(sargs)-1) != '\\') strcat(sargs,"\\");
512 if (!optB) WriteOut(MSG_Get("SHELL_CMD_DIR_INTRO"),sargs);
513
514 /* Command uses dta so set it to our internal dta */
515 RealPt save_dta=dos.dta();
516 dos.dta(dos.tables.tempdta);
517 DOS_DTA dta(dos.dta());
518 bool ret=DOS_FindFirst(args,0xffff & ~DOS_ATTR_VOLUME);
519 if (!ret) {
520 if (!optB) WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),args);
521 dos.dta(save_dta);
522 return;
523 }
524
525 do { /* File name and extension */
526 char name[DOS_NAMELENGTH_ASCII], lname[LFN_NAMELENGTH+1];
527 Bit32u size;Bit16u date;Bit16u time;Bit8u attr;
528 dta.GetResult(name,lname,size,date,time,attr);
529
530 /* Skip non-directories if option AD is present */
531 if(optAD && !(attr&DOS_ATTR_DIRECTORY) ) continue;
532
533 /* output the file */
534 if (optB) {
535 // this overrides pretty much everything
536 if (strcmp(".",uselfn?lname:name) && strcmp("..",uselfn?lname:name)) {
537 WriteOut("%s\n",uselfn?lname:name);
538 }
539 } else {
540 char * ext = empty_string;
541 if (!optW && (name[0] != '.')) {
542 ext = strrchr(name, '.');
543 if (!ext) ext = empty_string;
544 else *ext++ = 0;
545 }
546 Bit8u day = (Bit8u)(date & 0x001f);
547 Bit8u month = (Bit8u)((date >> 5) & 0x000f);
548 Bit16u year = (Bit16u)((date >> 9) + 1980);
549 Bit8u hour = (Bit8u)((time >> 5 ) >> 6);
550 Bit8u minute = (Bit8u)((time >> 5) & 0x003f);
551
552 if (attr & DOS_ATTR_DIRECTORY) {
553 if (optW) {
554 WriteOut("[%s]",name);
555 size_t namelen = strlen(name);
556 if (namelen <= 14) {
557 for (size_t i=14-namelen;i>0;i--) WriteOut(" ");
558 }
559 } else {
560 WriteOut("%-8s %-3s %-16s %02d-%02d-%04d %2d:%02d %s\n",name,ext,"<DIR>",day,month,year,hour,minute,uselfn?lname:"");
561 }
562 dir_count++;
563 } else {
564 if (optW) {
565 WriteOut("%-16s",name);
566 } else {
567 FormatNumber(size,numformat);
568 WriteOut("%-8s %-3s %16s %02d-%02d-%04d %2d:%02d %s\n",name,ext,numformat,day,month,year,hour,minute,uselfn?lname:"");
569 }
570 file_count++;
571 byte_count+=size;
572 }
573 if (optW) {
574 w_count++;
575 }
576 }
577 if (optP && !(++p_count%(22*w_size))) {
578 CMD_PAUSE(empty_string);
579 }
580 } while ( (ret=DOS_FindNext()) );
581 if (optW) {
582 if (w_count%5) WriteOut("\n");
583 }
584 if (!optB) {
585 /* Show the summary of results */
586 FormatNumber(byte_count,numformat);
587 WriteOut(MSG_Get("SHELL_CMD_DIR_BYTES_USED"),file_count,numformat);
588 Bit8u drive=dta.GetSearchDrive();
589 //TODO Free Space
590 Bitu free_space=1024*1024*100;
591 if (Drives[drive]) {
592 Bit16u bytes_sector;Bit8u sectors_cluster;Bit16u total_clusters;Bit16u free_clusters;
593 Drives[drive]->AllocationInfo(&bytes_sector,§ors_cluster,&total_clusters,&free_clusters);
594 free_space=bytes_sector*sectors_cluster*free_clusters;
595 }
596 FormatNumber(free_space,numformat);
597 WriteOut(MSG_Get("SHELL_CMD_DIR_BYTES_FREE"),dir_count,numformat);
598 }
599 dos.dta(save_dta);
600 }
601
602 struct copysource {
603 std::string filename;
604 bool concat;
copysourcecopysource605 copysource(std::string filein,bool concatin):
606 filename(filein),concat(concatin){ };
copysourcecopysource607 copysource():filename(""),concat(false){ };
608 };
609
610
CMD_COPY(char * args)611 void DOS_Shell::CMD_COPY(char * args) {
612 HELP("COPY");
613 static char defaulttarget[] = ".";
614 StripSpaces(args);
615 /* Command uses dta so set it to our internal dta */
616 RealPt save_dta=dos.dta();
617 dos.dta(dos.tables.tempdta);
618 DOS_DTA dta(dos.dta());
619 Bit32u size;Bit16u date;Bit16u time;Bit8u attr;
620 char name[DOS_NAMELENGTH_ASCII], lname[LFN_NAMELENGTH+1];
621 std::vector<copysource> sources;
622 // ignore /b and /t switches: always copy binary
623 while(ScanCMDBool(args,"B")) ;
624 while(ScanCMDBool(args,"T")) ; //Shouldn't this be A ?
625 while(ScanCMDBool(args,"A")) ;
626 ScanCMDBool(args,"Y");
627 ScanCMDBool(args,"-Y");
628 ScanCMDBool(args,"V");
629
630 char * rem=ScanCMDRemain(args);
631 if (rem) {
632 WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
633 dos.dta(save_dta);
634 return;
635 }
636 // Gather all sources (extension to copy more then 1 file specified at command line)
637 // Concatenating files go as follows: All parts except for the last bear the concat flag.
638 // This construction allows them to be counted (only the non concat set)
639 char q[]="\"";
640 char* source_p = NULL;
641 char source_x[DOS_PATHLENGTH+CROSS_LEN];
642 while ( (source_p = StripArg(args)) && *source_p ) {
643 do {
644 char* plus = strchr(source_p,'+');
645 // If StripWord() previously cut at a space before a plus then
646 // set concatenate flag on last source and remove leading plus.
647 if (plus == source_p && sources.size()) {
648 sources[sources.size()-1].concat = true;
649 // If spaces also followed plus then item is only a plus.
650 if (strlen(++source_p)==0) break;
651 plus = strchr(source_p,'+');
652 }
653 if (plus) *plus++ = 0;
654 safe_strncpy(source_x,source_p,CROSS_LEN);
655 bool has_drive_spec = false;
656 size_t source_x_len = strlen(source_x);
657 if (source_x_len>0) {
658 if (source_x[source_x_len-1]==':') has_drive_spec = true;
659 }
660 if (!has_drive_spec && !strpbrk(source_p,"*?") ) { //doubt that fu*\*.* is valid
661 char spath[DOS_PATHLENGTH];
662 if (DOS_GetSFNPath(source_p,spath,false) && DOS_FindFirst(spath,0xffff & ~DOS_ATTR_VOLUME)) {
663 dta.GetResult(name,lname,size,date,time,attr);
664 if (attr & DOS_ATTR_DIRECTORY)
665 strcat(source_x,"\\*.*");
666 }
667 }
668 sources.push_back(copysource(source_x,(plus)?true:false));
669 source_p = plus;
670 } while(source_p && *source_p);
671 }
672 // At least one source has to be there
673 if (!sources.size() || !sources[0].filename.size()) {
674 WriteOut(MSG_Get("SHELL_MISSING_PARAMETER"));
675 dos.dta(save_dta);
676 return;
677 };
678
679 copysource target;
680 // If more then one object exists and last target is not part of a
681 // concat sequence then make it the target.
682 if(sources.size()>1 && !sources[sources.size()-2].concat){
683 target = sources.back();
684 sources.pop_back();
685 }
686 //If no target => default target with concat flag true to detect a+b+c
687 if(target.filename.size() == 0) target = copysource(defaulttarget,true);
688
689 copysource oldsource;
690 copysource source;
691 Bit32u count = 0;
692 while(sources.size()) {
693 /* Get next source item and keep track of old source for concat start end */
694 oldsource = source;
695 source = sources[0];
696 sources.erase(sources.begin());
697
698 //Skip first file if doing a+b+c. Set target to first file
699 if(!oldsource.concat && source.concat && target.concat) {
700 target = source;
701 continue;
702 }
703
704 /* Make a full path in the args */
705 char pathSourcePre[DOS_PATHLENGTH], pathSource[DOS_PATHLENGTH+2];
706 char pathTarget[DOS_PATHLENGTH];
707
708 if (!DOS_Canonicalize(const_cast<char*>(source.filename.c_str()),pathSourcePre)) {
709 WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
710 dos.dta(save_dta);
711 return;
712 }
713 strcpy(pathSource,pathSourcePre);
714 if (uselfn) sprintf(pathSource,"\"%s\"",pathSourcePre);
715 // cut search pattern
716 char* pos = strrchr(pathSource,'\\');
717 if (pos) *(pos+1) = 0;
718
719 if (!DOS_Canonicalize(const_cast<char*>(target.filename.c_str()),pathTarget)) {
720 WriteOut(MSG_Get("SHELL_ILLEGAL_PATH"));
721 dos.dta(save_dta);
722 return;
723 }
724 char* temp = strstr(pathTarget,"*.*");
725 if(temp) *temp = 0;//strip off *.* from target
726
727 // add '\\' if target is a directory
728 bool target_is_file = true;
729 if (pathTarget[strlen(pathTarget)-1]!='\\') {
730 if (DOS_FindFirst(pathTarget,0xffff & ~DOS_ATTR_VOLUME)) {
731 dta.GetResult(name,lname,size,date,time,attr);
732 if (attr & DOS_ATTR_DIRECTORY) {
733 strcat(pathTarget,"\\");
734 target_is_file = false;
735 }
736 }
737 } else target_is_file = false;
738
739 //Find first sourcefile
740 char sPath[DOS_PATHLENGTH];
741 bool ret = DOS_GetSFNPath(source.filename.c_str(),sPath,false) && DOS_FindFirst(const_cast<char*>(sPath),0xffff & ~DOS_ATTR_VOLUME);
742 if (!ret) {
743 WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),const_cast<char*>(source.filename.c_str()));
744 dos.dta(save_dta);
745 return;
746 }
747
748 Bit16u sourceHandle,targetHandle;
749 char nameTarget[DOS_PATHLENGTH];
750 char nameSource[DOS_PATHLENGTH];
751
752 bool second_file_of_current_source = false;
753 while (ret) {
754 dta.GetResult(name,lname,size,date,time,attr);
755
756 if ((attr & DOS_ATTR_DIRECTORY)==0) {
757 strcpy(nameSource,pathSource);
758 strcat(nameSource,name);
759 // Open Source
760 if (DOS_OpenFile(nameSource,0,&sourceHandle)) {
761 // Create Target or open it if in concat mode
762 strcpy(nameTarget,q);
763 strcat(nameTarget,pathTarget);
764 if (nameTarget[strlen(nameTarget)-1]=='\\') strcat(nameTarget,uselfn?lname:name);
765 strcat(nameTarget,q);
766
767 //Special variable to ensure that copy * a_file, where a_file is not a directory concats.
768 bool special = second_file_of_current_source && target_is_file;
769 second_file_of_current_source = true;
770 if (special) oldsource.concat = true;
771 //Don't create a new file when in concat mode
772 if (oldsource.concat || DOS_CreateFile(nameTarget,0,&targetHandle)) {
773 Bit32u dummy=0;
774 //In concat mode. Open the target and seek to the eof
775 if (!oldsource.concat || (DOS_OpenFile(nameTarget,OPEN_READWRITE,&targetHandle) &&
776 DOS_SeekFile(targetHandle,&dummy,DOS_SEEK_END))) {
777 // Copy
778 static Bit8u buffer[0x8000]; // static, otherwise stack overflow possible.
779 bool failed = false;
780 Bit16u toread = 0x8000;
781 do {
782 failed |= DOS_ReadFile(sourceHandle,buffer,&toread);
783 failed |= DOS_WriteFile(targetHandle,buffer,&toread);
784 } while (toread==0x8000);
785 failed |= DOS_CloseFile(sourceHandle);
786 failed |= DOS_CloseFile(targetHandle);
787 if (strcmp(name,lname)&&uselfn)
788 WriteOut(" %s [%s]\n",lname,name);
789 else
790 WriteOut(" %s\n",uselfn?lname:name);
791 if(!source.concat && !special) count++; //Only count concat files once
792 } else {
793 DOS_CloseFile(sourceHandle);
794 WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast<char*>(target.filename.c_str()));
795 }
796 } else {
797 DOS_CloseFile(sourceHandle);
798 WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast<char*>(target.filename.c_str()));
799 }
800 } else WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast<char*>(source.filename.c_str()));
801 };
802 //On to the next file if the previous one wasn't a device
803 if ((attr&DOS_ATTR_DEVICE) == 0) ret = DOS_FindNext();
804 else ret = false;
805 };
806 }
807
808 WriteOut(MSG_Get("SHELL_CMD_COPY_SUCCESS"),count);
809 dos.dta(save_dta);
810 }
811
CMD_SET(char * args)812 void DOS_Shell::CMD_SET(char * args) {
813 HELP("SET");
814 StripSpaces(args);
815 std::string line;
816 if (!*args) {
817 /* No command line show all environment lines */
818 Bitu count=GetEnvCount();
819 for (Bitu a=0;a<count;a++) {
820 if (GetEnvNum(a,line)) WriteOut("%s\n",line.c_str());
821 }
822 return;
823 }
824 //There are args:
825 char * pcheck = args;
826 while ( *pcheck && (*pcheck == ' ' || *pcheck == '\t')) pcheck++;
827 if (*pcheck && strlen(pcheck) >3 && (strncasecmp(pcheck,"/p ",3) == 0)) E_Exit("Set /P is not supported. Use Choice!");
828
829 char * p=strpbrk(args, "=");
830 if (!p) {
831 if (!GetEnvStr(args,line)) WriteOut(MSG_Get("SHELL_CMD_SET_NOT_SET"),args);
832 WriteOut("%s\n",line.c_str());
833 } else {
834 *p++=0;
835 /* parse p for envirionment variables */
836 char parsed[CMD_MAXLINE];
837 char* p_parsed = parsed;
838 while(*p) {
839 if(*p != '%') *p_parsed++ = *p++; //Just add it (most likely path)
840 else if( *(p+1) == '%') {
841 *p_parsed++ = '%'; p += 2; //%% => %
842 } else {
843 char * second = strchr(++p,'%');
844 if(!second) continue; *second++ = 0;
845 std::string temp;
846 if (GetEnvStr(p,temp)) {
847 std::string::size_type equals = temp.find('=');
848 if (equals == std::string::npos) continue;
849 strcpy(p_parsed,temp.substr(equals+1).c_str());
850 p_parsed += strlen(p_parsed);
851 }
852 p = second;
853 }
854 }
855 *p_parsed = 0;
856 /* Try setting the variable */
857 if (!SetEnv(args,parsed)) {
858 WriteOut(MSG_Get("SHELL_CMD_SET_OUT_OF_SPACE"));
859 }
860 }
861 }
862
CMD_IF(char * args)863 void DOS_Shell::CMD_IF(char * args) {
864 HELP("IF");
865 StripSpaces(args,'=');
866 bool has_not=false;
867
868 while (strncasecmp(args,"NOT",3) == 0) {
869 if (!isspace(*reinterpret_cast<unsigned char*>(&args[3])) && (args[3] != '=')) break;
870 args += 3; //skip text
871 //skip more spaces
872 StripSpaces(args,'=');
873 has_not = !has_not;
874 }
875
876 if(strncasecmp(args,"ERRORLEVEL",10) == 0) {
877 args += 10; //skip text
878 //Strip spaces and ==
879 StripSpaces(args,'=');
880 char* word = StripWord(args);
881 if(!isdigit(*word)) {
882 WriteOut(MSG_Get("SHELL_CMD_IF_ERRORLEVEL_MISSING_NUMBER"));
883 return;
884 }
885
886 Bit8u n = 0;
887 do n = n * 10 + (*word - '0');
888 while (isdigit(*++word));
889 if(*word && !isspace(*word)) {
890 WriteOut(MSG_Get("SHELL_CMD_IF_ERRORLEVEL_INVALID_NUMBER"));
891 return;
892 }
893 /* Read the error code from DOS */
894 if ((dos.return_code>=n) ==(!has_not)) DoCommand(args);
895 return;
896 }
897
898 if(strncasecmp(args,"EXIST ",6) == 0) {
899 args += 6; //Skip text
900 StripSpaces(args);
901 char* word = StripArg(args);
902 if (!*word) {
903 WriteOut(MSG_Get("SHELL_CMD_IF_EXIST_MISSING_FILENAME"));
904 return;
905 }
906
907 { /* DOS_FindFirst uses dta so set it to our internal dta */
908 RealPt save_dta=dos.dta();
909 dos.dta(dos.tables.tempdta);
910 bool ret=DOS_FindFirst(word,0xffff & ~DOS_ATTR_VOLUME);
911 dos.dta(save_dta);
912 if (ret==(!has_not)) DoCommand(args);
913 }
914 return;
915 }
916
917 /* Normal if string compare */
918
919 char* word1 = args;
920 // first word is until space or =
921 while (*args && !isspace(*reinterpret_cast<unsigned char*>(args)) && (*args != '='))
922 args++;
923 char* end_word1 = args;
924
925 // scan for =
926 while (*args && (*args != '='))
927 args++;
928 // check for ==
929 if ((*args==0) || (args[1] != '=')) {
930 SyntaxError();
931 return;
932 }
933 args += 2;
934 StripSpaces(args,'=');
935
936 char* word2 = args;
937 // second word is until space or =
938 while (*args && !isspace(*reinterpret_cast<unsigned char*>(args)) && (*args != '='))
939 args++;
940
941 if (*args) {
942 *end_word1 = 0; // mark end of first word
943 *args++ = 0; // mark end of second word
944 StripSpaces(args,'=');
945
946 if ((strcmp(word1,word2)==0)==(!has_not)) DoCommand(args);
947 }
948 }
949
CMD_GOTO(char * args)950 void DOS_Shell::CMD_GOTO(char * args) {
951 HELP("GOTO");
952 StripSpaces(args);
953 if (!bf) return;
954 if (*args &&(*args==':')) args++;
955 //label ends at the first space
956 char* non_space = args;
957 while (*non_space) {
958 if((*non_space == ' ') || (*non_space == '\t'))
959 *non_space = 0;
960 else non_space++;
961 }
962 if (!*args) {
963 WriteOut(MSG_Get("SHELL_CMD_GOTO_MISSING_LABEL"));
964 return;
965 }
966 if (!bf->Goto(args)) {
967 WriteOut(MSG_Get("SHELL_CMD_GOTO_LABEL_NOT_FOUND"),args);
968 return;
969 }
970 }
971
CMD_SHIFT(char * args)972 void DOS_Shell::CMD_SHIFT(char * args ) {
973 HELP("SHIFT");
974 if(bf) bf->Shift();
975 }
976
CMD_TYPE(char * args)977 void DOS_Shell::CMD_TYPE(char * args) {
978 HELP("TYPE");
979 StripSpaces(args);
980 if (!*args) {
981 WriteOut(MSG_Get("SHELL_SYNTAXERROR"));
982 return;
983 }
984 Bit16u handle;
985 char * word;
986 nextfile:
987 word=StripArg(args);
988 if (!DOS_OpenFile(word,0,&handle)) {
989 WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),word);
990 return;
991 }
992 Bit16u n;Bit8u c;
993 do {
994 n=1;
995 DOS_ReadFile(handle,&c,&n);
996 if (c==0x1a) break; // stop at EOF
997 DOS_WriteFile(STDOUT,&c,&n);
998 } while (n);
999 DOS_CloseFile(handle);
1000 if (*args) goto nextfile;
1001 }
1002
CMD_REM(char * args)1003 void DOS_Shell::CMD_REM(char * args) {
1004 HELP("REM");
1005 }
1006
CMD_PAUSE(char * args)1007 void DOS_Shell::CMD_PAUSE(char * args){
1008 HELP("PAUSE");
1009 WriteOut(MSG_Get("SHELL_CMD_PAUSE"));
1010 Bit8u c;Bit16u n=1;
1011 DOS_ReadFile(STDIN,&c,&n);
1012 if (c==0) DOS_ReadFile(STDIN,&c,&n); // read extended key
1013 }
1014
CMD_CALL(char * args)1015 void DOS_Shell::CMD_CALL(char * args){
1016 HELP("CALL");
1017 this->call=true; /* else the old batchfile will be closed first */
1018 this->ParseLine(args);
1019 this->call=false;
1020 }
1021
CMD_DATE(char * args)1022 void DOS_Shell::CMD_DATE(char * args) {
1023 HELP("DATE");
1024 if(ScanCMDBool(args,"H")) {
1025 // synchronize date with host parameter
1026 time_t curtime;
1027 struct tm *loctime;
1028 curtime = time (NULL);
1029 loctime = localtime (&curtime);
1030
1031 reg_cx = loctime->tm_year+1900;
1032 reg_dh = loctime->tm_mon+1;
1033 reg_dl = loctime->tm_mday;
1034
1035 reg_ah=0x2b; // set system date
1036 CALLBACK_RunRealInt(0x21);
1037 return;
1038 }
1039 // check if a date was passed in command line
1040 Bit32u newday,newmonth,newyear;
1041 if(sscanf(args,"%u-%u-%u",&newmonth,&newday,&newyear)==3) {
1042 reg_cx = static_cast<Bit16u>(newyear);
1043 reg_dh = static_cast<Bit8u>(newmonth);
1044 reg_dl = static_cast<Bit8u>(newday);
1045
1046 reg_ah=0x2b; // set system date
1047 CALLBACK_RunRealInt(0x21);
1048 if(reg_al==0xff) WriteOut(MSG_Get("SHELL_CMD_DATE_ERROR"));
1049 return;
1050 }
1051 // display the current date
1052 reg_ah=0x2a; // get system date
1053 CALLBACK_RunRealInt(0x21);
1054
1055 const char* datestring = MSG_Get("SHELL_CMD_DATE_DAYS");
1056 Bit32u length;
1057 char day[6] = {0};
1058 if(sscanf(datestring,"%u",&length) && (length<5) && (strlen(datestring)==(length*7+1))) {
1059 // date string appears valid
1060 for(Bit32u i = 0; i < length; i++) day[i] = datestring[reg_al*length+1+i];
1061 }
1062 bool dateonly = ScanCMDBool(args,"T");
1063 if(!dateonly) WriteOut(MSG_Get("SHELL_CMD_DATE_NOW"));
1064
1065 const char* formatstring = MSG_Get("SHELL_CMD_DATE_FORMAT");
1066 if(strlen(formatstring)!=5) return;
1067 char buffer[15] = {0};
1068 Bitu bufferptr=0;
1069 for(Bitu i = 0; i < 5; i++) {
1070 if(i==1 || i==3) {
1071 buffer[bufferptr] = formatstring[i];
1072 bufferptr++;
1073 } else {
1074 if(formatstring[i]=='M') bufferptr += sprintf(buffer+bufferptr,"%02u",(Bit8u) reg_dh);
1075 if(formatstring[i]=='D') bufferptr += sprintf(buffer+bufferptr,"%02u",(Bit8u) reg_dl);
1076 if(formatstring[i]=='Y') bufferptr += sprintf(buffer+bufferptr,"%04u",(Bit16u) reg_cx);
1077 }
1078 }
1079 WriteOut("%s %s\n",day, buffer);
1080 if(!dateonly) WriteOut(MSG_Get("SHELL_CMD_DATE_SETHLP"));
1081 };
1082
CMD_TIME(char * args)1083 void DOS_Shell::CMD_TIME(char * args) {
1084 HELP("TIME");
1085 if(ScanCMDBool(args,"H")) {
1086 // synchronize time with host parameter
1087 time_t curtime;
1088 struct tm *loctime;
1089 curtime = time (NULL);
1090 loctime = localtime (&curtime);
1091
1092 //reg_cx = loctime->;
1093 //reg_dh = loctime->;
1094 //reg_dl = loctime->;
1095
1096 // reg_ah=0x2d; // set system time TODO
1097 // CALLBACK_RunRealInt(0x21);
1098
1099 Bit32u ticks=(Bit32u)(((double)(loctime->tm_hour*3600+
1100 loctime->tm_min*60+
1101 loctime->tm_sec))*18.206481481);
1102 mem_writed(BIOS_TIMER,ticks);
1103 return;
1104 }
1105 bool timeonly = ScanCMDBool(args,"T");
1106
1107 reg_ah=0x2c; // get system time
1108 CALLBACK_RunRealInt(0x21);
1109 /*
1110 reg_dl= // 1/100 seconds
1111 reg_dh= // seconds
1112 reg_cl= // minutes
1113 reg_ch= // hours
1114 */
1115 if(timeonly) {
1116 WriteOut("%2u:%02u\n",reg_ch,reg_cl);
1117 } else {
1118 WriteOut(MSG_Get("SHELL_CMD_TIME_NOW"));
1119 WriteOut("%2u:%02u:%02u,%02u\n",reg_ch,reg_cl,reg_dh,reg_dl);
1120 }
1121 };
1122
CMD_SUBST(char * args)1123 void DOS_Shell::CMD_SUBST (char * args) {
1124 /* If more that one type can be substed think of something else
1125 * E.g. make basedir member dos_drive instead of localdrive
1126 */
1127 HELP("SUBST");
1128 localDrive* ldp=0;
1129 char mountstring[DOS_PATHLENGTH+CROSS_LEN+20];
1130 char temp_str[2] = { 0,0 };
1131 try {
1132 strcpy(mountstring,"MOUNT ");
1133 StripSpaces(args);
1134 std::string arg;
1135 CommandLine command(0,args);
1136
1137 if (command.GetCount() != 2) throw 0 ;
1138
1139 command.FindCommand(1,arg);
1140 if( (arg.size()>1) && arg[1] !=':') throw(0);
1141 temp_str[0]=(char)toupper(args[0]);
1142 command.FindCommand(2,arg);
1143 if((arg=="/D") || (arg=="/d")) {
1144 if(!Drives[temp_str[0]-'A'] ) throw 1; //targetdrive not in use
1145 strcat(mountstring,"-u ");
1146 strcat(mountstring,temp_str);
1147 this->ParseLine(mountstring);
1148 return;
1149 }
1150 if(Drives[temp_str[0]-'A'] ) throw 0; //targetdrive in use
1151 strcat(mountstring,temp_str);
1152 strcat(mountstring," ");
1153
1154 Bit8u drive;char dir[DOS_PATHLENGTH+2],fulldir[DOS_PATHLENGTH];
1155 if (strchr(arg.c_str(),'\"')==NULL)
1156 sprintf(dir,"\"%s\"",arg.c_str());
1157 else strcpy(dir,arg.c_str());
1158 if (!DOS_MakeName(dir,fulldir,&drive)) throw 0;
1159
1160 if( ( ldp=dynamic_cast<localDrive*>(Drives[drive])) == 0 ) throw 0;
1161 char newname[CROSS_LEN];
1162 strcpy(newname, ldp->basedir);
1163 strcat(newname,fulldir);
1164 CROSS_FILENAME(newname);
1165 ldp->dirCache.ExpandName(newname);
1166 strcat(mountstring,"\"");
1167 strcat(mountstring, newname);
1168 strcat(mountstring,"\"");
1169 this->ParseLine(mountstring);
1170 }
1171 catch(int a){
1172 if(a == 0) {
1173 WriteOut(MSG_Get("SHELL_CMD_SUBST_FAILURE"));
1174 } else {
1175 WriteOut(MSG_Get("SHELL_CMD_SUBST_NO_REMOVE"));
1176 }
1177 return;
1178 }
1179 catch(...) { //dynamic cast failed =>so no localdrive
1180 WriteOut(MSG_Get("SHELL_CMD_SUBST_FAILURE"));
1181 return;
1182 }
1183
1184 return;
1185 }
1186
CMD_LOADHIGH(char * args)1187 void DOS_Shell::CMD_LOADHIGH(char *args){
1188 HELP("LOADHIGH");
1189 Bit16u umb_start=dos_infoblock.GetStartOfUMBChain();
1190 Bit8u umb_flag=dos_infoblock.GetUMBChainState();
1191 Bit8u old_memstrat=(Bit8u)(DOS_GetMemAllocStrategy()&0xff);
1192 if (umb_start==0x9fff) {
1193 if ((umb_flag&1)==0) DOS_LinkUMBsToMemChain(1);
1194 DOS_SetMemAllocStrategy(0x80); // search in UMBs first
1195 this->ParseLine(args);
1196 Bit8u current_umb_flag=dos_infoblock.GetUMBChainState();
1197 if ((current_umb_flag&1)!=(umb_flag&1)) DOS_LinkUMBsToMemChain(umb_flag);
1198 DOS_SetMemAllocStrategy(old_memstrat); // restore strategy
1199 } else this->ParseLine(args);
1200 }
1201
CMD_CHOICE(char * args)1202 void DOS_Shell::CMD_CHOICE(char * args){
1203 HELP("CHOICE");
1204 static char defchoice[3] = {'y','n',0};
1205 char *rem = NULL, *ptr;
1206 bool optN = ScanCMDBool(args,"N");
1207 bool optS = ScanCMDBool(args,"S"); //Case-sensitive matching
1208 ScanCMDBool(args,"T"); //Default Choice after timeout
1209 if (args) {
1210 char *last = strchr(args,0);
1211 StripSpaces(args);
1212 rem = ScanCMDRemain(args);
1213 if (rem && *rem && (tolower(rem[1]) != 'c')) {
1214 WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem);
1215 return;
1216 }
1217 if (args == rem) args = strchr(rem,0)+1;
1218 if (rem) rem += 2;
1219 if(rem && rem[0]==':') rem++; /* optional : after /c */
1220 if (args > last) args = NULL;
1221 }
1222 if (!rem || !*rem) rem = defchoice; /* No choices specified use YN */
1223 ptr = rem;
1224 Bit8u c;
1225 if(!optS) while ((c = *ptr)) *ptr++ = (char)toupper(c); /* When in no case-sensitive mode. make everything upcase */
1226 if(args && *args ) {
1227 StripSpaces(args);
1228 size_t argslen = strlen(args);
1229 if(argslen>1 && args[0] == '"' && args[argslen-1] =='"') {
1230 args[argslen-1] = 0; //Remove quotes
1231 args++;
1232 }
1233 WriteOut(args);
1234 }
1235 /* Show question prompt of the form [a,b]? where a b are the choice values */
1236 if (!optN) {
1237 if(args && *args) WriteOut(" ");
1238 WriteOut("[");
1239 size_t len = strlen(rem);
1240 for(size_t t = 1; t < len; t++) {
1241 WriteOut("%c,",rem[t-1]);
1242 }
1243 WriteOut("%c]?",rem[len-1]);
1244 }
1245
1246 Bit16u n=1;
1247 do {
1248 DOS_ReadFile (STDIN,&c,&n);
1249 } while (!c || !(ptr = strchr(rem,(optS?c:toupper(c)))));
1250 c = optS?c:(Bit8u)toupper(c);
1251 DOS_WriteFile (STDOUT,&c, &n);
1252 dos.return_code = (Bit8u)(ptr-rem+1);
1253 }
1254
CMD_ATTRIB(char * args)1255 void DOS_Shell::CMD_ATTRIB(char *args){
1256 HELP("ATTRIB");
1257 // No-Op for now.
1258 }
1259
CMD_PATH(char * args)1260 void DOS_Shell::CMD_PATH(char *args){
1261 HELP("PATH");
1262 if(args && *args && strlen(args)){
1263 char pathstring[DOS_PATHLENGTH+CROSS_LEN+20]={ 0 };
1264 strcpy(pathstring,"set PATH=");
1265 while(args && *args && (*args=='='|| *args==' '))
1266 args++;
1267 strcat(pathstring,args);
1268 this->ParseLine(pathstring);
1269 return;
1270 } else {
1271 std::string line;
1272 if(GetEnvStr("PATH",line)) {
1273 WriteOut("%s",line.c_str());
1274 } else {
1275 WriteOut("PATH=(null)");
1276 }
1277 }
1278 }
1279
CMD_VER(char * args)1280 void DOS_Shell::CMD_VER(char *args) {
1281 HELP("VER");
1282 if(args && *args) {
1283 char* word = StripWord(args);
1284 if(strcasecmp(word,"set")) return;
1285 word = StripWord(args);
1286 if (!*args && !*word) { //Reset
1287 dos.version.major = 7;
1288 dos.version.minor = 10;
1289 } else if (*args == 0 && *word && (strchr(word,'.') != 0)) { //Allow: ver set 7.10
1290 const char * p = strchr(word,'.');
1291 dos.version.major = (Bit8u)(atoi(word));
1292 dos.version.minor = (Bit8u)(atoi(p+1));
1293 } else { //Official syntax: ver set 7 10
1294 dos.version.major = (Bit8u)(atoi(word));
1295 dos.version.minor = (Bit8u)(atoi(args));
1296 }
1297 if (autolfn) uselfn=dos.version.major>=7;
1298 } else WriteOut(MSG_Get("SHELL_CMD_VER_VER"),VERSION,dos.version.major,dos.version.minor,uselfn?"enabled":"disabled");
1299 }
1300