void SHELL_Init() { /* Add messages */ MSG_Add("SHELL_ILLEGAL_PATH","Illegal Path.\n"); MSG_Add("SHELL_CMD_HELP","If you want a list of all supported commands type \033[33;1mhelp /all\033[0m .\nA short list of the most often used commands:\n"); MSG_Add("SHELL_CMD_ECHO_ON","ECHO is on.\n"); MSG_Add("SHELL_CMD_ECHO_OFF","ECHO is off.\n"); MSG_Add("SHELL_ILLEGAL_SWITCH","Illegal switch: %s.\n"); MSG_Add("SHELL_MISSING_PARAMETER","Required parameter missing.\n"); MSG_Add("SHELL_CMD_CHDIR_ERROR","Unable to change to: %s.\n"); MSG_Add("SHELL_CMD_CHDIR_HINT","To change to different drive type \033[31m%c:\033[0m\n"); MSG_Add("SHELL_CMD_CHDIR_HINT_2","directoryname is longer than 8 characters and/or contains spaces.\nTry \033[31mcd %s\033[0m\n"); MSG_Add("SHELL_CMD_CHDIR_HINT_3","You are still on drive Z:, change to a mounted drive with \033[31mC:\033[0m.\n"); MSG_Add("SHELL_CMD_DATE_HELP","Displays or changes the internal date.\n"); MSG_Add("SHELL_CMD_DATE_ERROR","The specified date is not correct.\n"); MSG_Add("SHELL_CMD_DATE_DAYS","3SunMonTueWedThuFriSat"); // "2SoMoDiMiDoFrSa" MSG_Add("SHELL_CMD_DATE_NOW","Current date: "); MSG_Add("SHELL_CMD_DATE_SETHLP","Type 'date MM-DD-YYYY' to change.\n"); MSG_Add("SHELL_CMD_DATE_FORMAT","M/D/Y"); MSG_Add("SHELL_CMD_DATE_HELP_LONG","DATE [[/T] [/H] [/S] | MM-DD-YYYY]\n"\ " MM-DD-YYYY: new date to set\n"\ " /S: Permanently use host time and date as DOS time\n"\ " /F: Switch back to DOSBox internal time (opposite of /S)\n"\ " /T: Only display date\n"\ " /H: Synchronize with host\n"); MSG_Add("SHELL_CMD_TIME_HELP","Displays the internal time.\n"); MSG_Add("SHELL_CMD_TIME_NOW","Current time: "); MSG_Add("SHELL_CMD_TIME_HELP_LONG","TIME [/T] [/H]\n"\ " /T: Display simple time\n"\ " /H: Synchronize with host\n"); MSG_Add("SHELL_CMD_MKDIR_ERROR","Unable to make: %s.\n"); MSG_Add("SHELL_CMD_RMDIR_ERROR","Unable to remove: %s.\n"); MSG_Add("SHELL_CMD_DEL_ERROR","Unable to delete: %s.\n"); MSG_Add("SHELL_SYNTAXERROR","The syntax of the command is incorrect.\n"); MSG_Add("SHELL_CMD_SET_NOT_SET","Environment variable %s not defined.\n"); MSG_Add("SHELL_CMD_SET_OUT_OF_SPACE","Not enough environment space left.\n"); MSG_Add("SHELL_CMD_IF_EXIST_MISSING_FILENAME","IF EXIST: Missing filename.\n"); MSG_Add("SHELL_CMD_IF_ERRORLEVEL_MISSING_NUMBER","IF ERRORLEVEL: Missing number.\n"); MSG_Add("SHELL_CMD_IF_ERRORLEVEL_INVALID_NUMBER","IF ERRORLEVEL: Invalid number.\n"); MSG_Add("SHELL_CMD_GOTO_MISSING_LABEL","No label supplied to GOTO command.\n"); MSG_Add("SHELL_CMD_GOTO_LABEL_NOT_FOUND","GOTO: Label %s not found.\n"); MSG_Add("SHELL_CMD_FILE_NOT_FOUND","File %s not found.\n"); MSG_Add("SHELL_CMD_FILE_EXISTS","File %s already exists.\n"); MSG_Add("SHELL_CMD_DIR_INTRO","Directory of %s.\n"); MSG_Add("SHELL_CMD_DIR_BYTES_USED","%5d File(s) %17s Bytes.\n"); MSG_Add("SHELL_CMD_DIR_BYTES_FREE","%5d Dir(s) %17s Bytes free.\n"); MSG_Add("SHELL_EXECUTE_DRIVE_NOT_FOUND","Drive %c does not exist!\nYou must \033[31mmount\033[0m it first. Type \033[1;33mintro\033[0m or \033[1;33mintro mount\033[0m for more information.\n"); MSG_Add("SHELL_EXECUTE_ILLEGAL_COMMAND","Illegal command: %s.\n"); MSG_Add("SHELL_CMD_PAUSE","Press any key to continue.\n"); MSG_Add("SHELL_CMD_PAUSE_HELP","Waits for 1 keystroke to continue.\n"); MSG_Add("SHELL_CMD_COPY_FAILURE","Copy failure : %s.\n"); MSG_Add("SHELL_CMD_COPY_SUCCESS"," %d File(s) copied.\n"); MSG_Add("SHELL_CMD_SUBST_NO_REMOVE","Unable to remove, drive not in use.\n"); MSG_Add("SHELL_CMD_SUBST_FAILURE","SUBST failed. You either made an error in your commandline or the target drive is already used.\nIt's only possible to use SUBST on Local drives"); MSG_Add("SHELL_STARTUP_BEGIN", "\033[44;1m\xC9\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBB\n" "\xBA \033[32mWelcome to DOSBox %-8s\033[37m \xBA\n" "\xBA \xBA\n" // "\xBA DOSBox runs real and protected mode games. \xBA\n" "\xBA For a short introduction for new users type: \033[33mINTRO\033[37m \xBA\n" "\xBA For supported shell commands type: \033[33mHELP\033[37m \xBA\n" "\xBA \xBA\n" "\xBA To adjust the emulated CPU speed, use \033[31mctrl-F11\033[37m and \033[31mctrl-F12\033[37m. \xBA\n" "\xBA To activate the keymapper \033[31mctrl-F1\033[37m. \xBA\n" "\xBA For more information read the \033[36mREADME\033[37m file in the DOSBox directory. \xBA\n" "\xBA \xBA\n" ); MSG_Add("SHELL_STARTUP_CGA","\xBA DOSBox supports Composite CGA mode. \xBA\n" "\xBA Use \033[31mF12\033[37m to set composite output ON, OFF, or AUTO (default). \xBA\n" "\xBA \033[31m(Alt-)F11\033[37m changes hue; \033[31mctrl-alt-F11\033[37m selects early/late CGA model. \xBA\n" "\xBA \xBA\n" ); MSG_Add("SHELL_STARTUP_HERC","\xBA Use \033[31mF11\033[37m to cycle through white, amber, and green monochrome color. \xBA\n" "\xBA \xBA\n" ); MSG_Add("SHELL_STARTUP_DEBUG", "\xBA Press \033[31malt-Pause\033[37m to enter the debugger or start the exe with \033[33mDEBUG\033[37m. \xBA\n" "\xBA \xBA\n" ); MSG_Add("SHELL_STARTUP_END", "\xBA \033[32mHAVE FUN!\033[37m \xBA\n" "\xBA \033[32mThe DOSBox Team \033[33mhttp://www.dosbox.com\033[37m \xBA\n" "\xC8\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBC\033[0m\n" //"\n" //Breaks the startup message if you type a mount and a drive change. ); MSG_Add("SHELL_CMD_CHDIR_HELP","Displays/changes the current directory.\n"); MSG_Add("SHELL_CMD_CHDIR_HELP_LONG","CHDIR [drive:][path]\n" "CHDIR [..]\n" "CD [drive:][path]\n" "CD [..]\n\n" " .. Specifies that you want to change to the parent directory.\n\n" "Type CD drive: to display the current directory in the specified drive.\n" "Type CD without parameters to display the current drive and directory.\n"); MSG_Add("SHELL_CMD_CLS_HELP","Clear screen.\n"); MSG_Add("SHELL_CMD_DIR_HELP","Directory View.\n"); MSG_Add("SHELL_CMD_ECHO_HELP","Display messages and enable/disable command echoing.\n"); MSG_Add("SHELL_CMD_EXIT_HELP","Exit from the shell.\n"); MSG_Add("SHELL_CMD_HELP_HELP","Show help.\n"); MSG_Add("SHELL_CMD_MKDIR_HELP","Make Directory.\n"); MSG_Add("SHELL_CMD_MKDIR_HELP_LONG","MKDIR [drive:][path]\n" "MD [drive:][path]\n"); MSG_Add("SHELL_CMD_RMDIR_HELP","Remove Directory.\n"); MSG_Add("SHELL_CMD_RMDIR_HELP_LONG","RMDIR [drive:][path]\n" "RD [drive:][path]\n"); MSG_Add("SHELL_CMD_SET_HELP","Change environment variables.\n"); MSG_Add("SHELL_CMD_IF_HELP","Performs conditional processing in batch programs.\n"); MSG_Add("SHELL_CMD_GOTO_HELP","Jump to a labeled line in a batch script.\n"); MSG_Add("SHELL_CMD_SHIFT_HELP","Leftshift commandline parameters in a batch script.\n"); MSG_Add("SHELL_CMD_TYPE_HELP","Display the contents of a text-file.\n"); MSG_Add("SHELL_CMD_TYPE_HELP_LONG","TYPE [drive:][path][filename]\n"); MSG_Add("SHELL_CMD_REM_HELP","Add comments in a batch file.\n"); MSG_Add("SHELL_CMD_REM_HELP_LONG","REM [comment]\n"); MSG_Add("SHELL_CMD_NO_WILD","This is a simple version of the command, no wildcards allowed!\n"); MSG_Add("SHELL_CMD_RENAME_HELP","Renames one or more files.\n"); MSG_Add("SHELL_CMD_RENAME_HELP_LONG","RENAME [drive:][path]filename1 filename2.\n" "REN [drive:][path]filename1 filename2.\n\n" "Note that you can not specify a new drive or path for your destination file.\n"); MSG_Add("SHELL_CMD_DELETE_HELP","Removes one or more files.\n"); MSG_Add("SHELL_CMD_COPY_HELP","Copy files.\n"); MSG_Add("SHELL_CMD_CALL_HELP","Start a batch file from within another batch file.\n"); MSG_Add("SHELL_CMD_SUBST_HELP","Assign an internal directory to a drive.\n"); MSG_Add("SHELL_CMD_LOADHIGH_HELP","Loads a program into upper memory (requires xms=true,umb=true).\n"); MSG_Add("SHELL_CMD_CHOICE_HELP","Waits for a keypress and sets ERRORLEVEL.\n"); MSG_Add("SHELL_CMD_CHOICE_HELP_LONG","CHOICE [/C:choices] [/N] [/S] text\n" " /C[:]choices - Specifies allowable keys. Default is: yn.\n" " /N - Do not display the choices at end of prompt.\n" " /S - Enables case-sensitive choices to be selected.\n" " text - The text to display as a prompt.\n"); MSG_Add("SHELL_CMD_ATTRIB_HELP","Does nothing. Provided for compatibility.\n"); MSG_Add("SHELL_CMD_PATH_HELP","Provided for compatibility.\n"); MSG_Add("SHELL_CMD_VER_HELP","View and set the reported DOS version.\n"); MSG_Add("SHELL_CMD_VER_VER","DOSBox version %s. Reported DOS version %d.%02d.\n"); /* Regular startup */ call_shellstop=CALLBACK_Allocate(); /* Setup the startup CS:IP to kill the last running machine when exitted */ RealPt newcsip=CALLBACK_RealPointer(call_shellstop); SegSet16(cs,RealSeg(newcsip)); reg_ip=RealOff(newcsip); CALLBACK_Setup(call_shellstop,shellstop_handler,CB_IRET,"shell stop"); PROGRAMS_MakeFile("COMMAND.COM",SHELL_ProgramStart); /* Now call up the shell for the first time */ Bit16u psp_seg=DOS_FIRST_SHELL; Bit16u env_seg=DOS_FIRST_SHELL+19; //DOS_GetMemory(1+(4096/16))+1; Bit16u stack_seg=DOS_GetMemory(2048/16); SegSet16(ss,stack_seg); reg_sp=2046; /* Set up int 24 and psp (Telarium games) */ real_writeb(psp_seg+16+1,0,0xea); /* far jmp */ real_writed(psp_seg+16+1,1,real_readd(0,0x24*4)); real_writed(0,0x24*4,((Bit32u)psp_seg<<16) | ((16+1)<<4)); /* Set up int 23 to "int 20" in the psp. Fixes what.exe */ real_writed(0,0x23*4,((Bit32u)psp_seg<<16)); /* Setup MCBs */ DOS_MCB pspmcb((Bit16u)(psp_seg-1)); pspmcb.SetPSPSeg(psp_seg); // MCB of the command shell psp pspmcb.SetSize(0x10+2); pspmcb.SetType(0x4d); DOS_MCB envmcb((Bit16u)(env_seg-1)); envmcb.SetPSPSeg(psp_seg); // MCB of the command shell environment envmcb.SetSize(DOS_MEM_START-env_seg); envmcb.SetType(0x4d); /* Setup environment */ PhysPt env_write=PhysMake(env_seg,0); MEM_BlockWrite(env_write,path_string,(Bitu)(strlen(path_string)+1)); env_write += (PhysPt)(strlen(path_string)+1); MEM_BlockWrite(env_write,comspec_string,(Bitu)(strlen(comspec_string)+1)); env_write += (PhysPt)(strlen(comspec_string)+1); mem_writeb(env_write++,0); mem_writew(env_write,1); env_write+=2; MEM_BlockWrite(env_write,full_name,(Bitu)(strlen(full_name)+1)); DOS_PSP psp(psp_seg); psp.MakeNew(0); dos.psp(psp_seg); /* The start of the filetable in the psp must look like this: * 01 01 01 00 02 * In order to achieve this: First open 2 files. Close the first and * duplicate the second (so the entries get 01) */ Bit16u dummy=0; DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDIN */ DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDOUT */ DOS_CloseFile(0); /* Close STDIN */ DOS_ForceDuplicateEntry(1,0); /* "new" STDIN */ DOS_ForceDuplicateEntry(1,2); /* STDERR */ DOS_OpenFile("CON",OPEN_READWRITE,&dummy); /* STDAUX */ DOS_OpenFile("PRN",OPEN_READWRITE,&dummy); /* STDPRN */ psp.SetParent(psp_seg); /* Set the environment */ psp.SetEnvironment(env_seg); /* Set the command line for the shell start up */ CommandTail tail; tail.count=(Bit8u)strlen(init_line); strcpy(tail.buffer,init_line); MEM_BlockWrite(PhysMake(psp_seg,128),&tail,128); /* Setup internal DOS Variables */ dos.dta(RealMake(psp_seg,0x80)); dos.psp(psp_seg); SHELL_ProgramStart(&first_shell); #if DOSKIT current_shell = (DOS_Shell *)first_shell; #endif first_shell->Run(); delete first_shell; first_shell = 0;//Make clear that it shouldn't be used anymore }
void DOS_PSP::CloseFiles(void) { for (Bit16u i=0;i<sGet(sPSP,max_files);i++) { DOS_CloseFile(i); } }
bool BatchFile::ReadLine(char * line) { //Open the batchfile and seek to stored postion if (!DOS_OpenFile(filename.c_str(),(DOS_NOT_INHERIT|OPEN_READ),&file_handle)) { LOG(LOG_MISC,LOG_ERROR)("ReadLine Can't open BatchFile %s",filename.c_str()); delete this; return false; } DOS_SeekFile(file_handle,&(this->location),DOS_SEEK_SET); Bit8u c=0;Bit16u n=1; char temp[CMD_MAXLINE]; emptyline: char * cmd_write=temp; do { n=1; DOS_ReadFile(file_handle,&c,&n); if (n>0) { /* Why are we filtering this ? * Exclusion list: tab for batch files * escape for ansi * backspace for alien odyssey */ if (c>31 || c==0x1b || c=='\t' || c==8) { //Only add it if room for it (and trailing zero) in the buffer, but do the check here instead at the end //So we continue reading till EOL/EOF if (((cmd_write - temp) + 1) < (CMD_MAXLINE - 1)) *cmd_write++ = c; } } } while (c!='\n' && n); *cmd_write=0; if (!n && cmd_write==temp) { //Close file and delete bat file DOS_CloseFile(file_handle); delete this; return false; } if (!strlen(temp)) goto emptyline; if (temp[0]==':') goto emptyline; /* Now parse the line read from the bat file for % stuff */ cmd_write=line; char * cmd_read=temp; while (*cmd_read) { if (*cmd_read == '%') { cmd_read++; if (cmd_read[0] == '%') { cmd_read++; if (((cmd_write - line) + 1) < (CMD_MAXLINE - 1)) *cmd_write++ = '%'; continue; } if (cmd_read[0] == '0') { /* Handle %0 */ const char *file_name = cmd->GetFileName(); cmd_read++; size_t name_len = strlen(file_name); if (((cmd_write - line) + name_len) < (CMD_MAXLINE - 1)) { strcpy(cmd_write,file_name); cmd_write += name_len; } continue; } char next = cmd_read[0]; if(next > '0' && next <= '9') { /* Handle %1 %2 .. %9 */ cmd_read++; //Progress reader next -= '0'; if (cmd->GetCount()<(unsigned int)next) continue; std::string word; if (!cmd->FindCommand((unsigned int)next,word)) continue; size_t name_len = strlen(word.c_str()); if (((cmd_write - line) + name_len) < (CMD_MAXLINE - 1)) { strcpy(cmd_write,word.c_str()); cmd_write += name_len; } continue; } else { /* Not a command line number has to be an environment */ char * first = strchr(cmd_read,'%'); /* No env afterall. Ignore a single % */ if (!first) {/* *cmd_write++ = '%';*/continue;} *first++ = 0; std::string env; if (shell->GetEnvStr(cmd_read,env)) { const char* equals = strchr(env.c_str(),'='); if (!equals) continue; equals++; size_t name_len = strlen(equals); if (((cmd_write - line) + name_len) < (CMD_MAXLINE - 1)) { strcpy(cmd_write,equals); cmd_write += name_len; } } cmd_read = first; } } else { if (((cmd_write - line) + 1) < (CMD_MAXLINE - 1)) *cmd_write++ = *cmd_read++; } } *cmd_write = 0; //Store current location and close bat file this->location = 0; DOS_SeekFile(file_handle,&(this->location),DOS_SEEK_CUR); DOS_CloseFile(file_handle); return true; }
void DOS_Shell::ParseLine(char * line) { LOG(LOG_EXEC,LOG_ERROR)("Parsing command line: %s",line); /* Check for a leading @ */ if (line[0] == '@') line[0] = ' '; line = trim(line); /* Do redirection and pipe checks */ char * in = 0; char * out = 0; Bit16u dummy,dummy2; Bit32u bigdummy = 0; Bitu num = 0; /* Number of commands in this line */ bool append; bool normalstdin = false; /* wether stdin/out are open on start. */ bool normalstdout = false; /* Bug: Assumed is they are "con" */ num = GetRedirection(line,&in, &out,&append); if (num>1) LOG_MSG("SHELL:Multiple command on 1 line not supported"); if (in || out) { normalstdin = (psp->GetFileHandle(0) != 0xff); normalstdout = (psp->GetFileHandle(1) != 0xff); } if (in) { if(DOS_OpenFile(in,OPEN_READ,&dummy)) { //Test if file exists DOS_CloseFile(dummy); LOG_MSG("SHELL:Redirect input from %s",in); if(normalstdin) DOS_CloseFile(0); //Close stdin DOS_OpenFile(in,OPEN_READ,&dummy); //Open new stdin } } if (out){ LOG_MSG("SHELL:Redirect output to %s",out); if(normalstdout) DOS_CloseFile(1); if(!normalstdin && !in) DOS_OpenFile("con",OPEN_READWRITE,&dummy); bool status = true; /* Create if not exist. Open if exist. Both in read/write mode */ if(append) { if( (status = DOS_OpenFile(out,OPEN_READWRITE,&dummy)) ) { DOS_SeekFile(1,&bigdummy,DOS_SEEK_END); } else { status = DOS_CreateFile(out,DOS_ATTR_ARCHIVE,&dummy); //Create if not exists. } } else { status = DOS_OpenFileExtended(out,OPEN_READWRITE,DOS_ATTR_ARCHIVE,0x12,&dummy,&dummy2); } if(!status && normalstdout) DOS_OpenFile("con",OPEN_READWRITE,&dummy); //Read only file, open con again if(!normalstdin && !in) DOS_CloseFile(0); } /* Run the actual command */ DoCommand(line); /* Restore handles */ if(in) { DOS_CloseFile(0); if(normalstdin) DOS_OpenFile("con",OPEN_READWRITE,&dummy); free(in); } if(out) { DOS_CloseFile(1); if(!normalstdin) DOS_OpenFile("con",OPEN_READWRITE,&dummy); if(normalstdout) DOS_OpenFile("con",OPEN_READWRITE,&dummy); if(!normalstdin) DOS_CloseFile(0); free(out); } }
static Bitu DOS_21Handler(void) { if (((reg_ah != 0x50) && (reg_ah != 0x51) && (reg_ah != 0x62) && (reg_ah != 0x64)) && (reg_ah<0x6c)) { DOS_PSP psp(dos.psp()); psp.SetStack(RealMake(SegValue(ss),reg_sp-18)); } char name1[DOSNAMEBUF+2+DOS_NAMELENGTH_ASCII]; char name2[DOSNAMEBUF+2+DOS_NAMELENGTH_ASCII]; char *p; static Bitu time_start = 0; //For emulating temporary time changes. switch (reg_ah) { case 0x00: /* Terminate Program */ DOS_Terminate(mem_readw(SegPhys(ss)+reg_sp+2),false,0); break; case 0x01: /* Read character from STDIN, with echo */ { Bit8u c;Bit16u n=1; dos.echo=true; DOS_ReadFile(STDIN,&c,&n); reg_al=c; dos.echo=false; } break; case 0x02: /* Write character to STDOUT */ { Bit8u c=reg_dl;Bit16u n=1; DOS_WriteFile(STDOUT,&c,&n); //Not in the official specs, but happens nonetheless. (last written character) reg_al=(c==9)?0x20:c; //strangely, tab conversion to spaces is reflected here } break; case 0x03: /* Read character from STDAUX */ { Bit16u port = real_readw(0x40,0); if(port!=0 && serialports[0]) { Bit8u status; // RTS/DTR on IO_WriteB(port+4,0x3); serialports[0]->Getchar(®_al, &status, true, 0xFFFFFFFF); } } break; case 0x04: /* Write Character to STDAUX */ { Bit16u port = real_readw(0x40,0); if(port!=0 && serialports[0]) { // RTS/DTR on IO_WriteB(port+4,0x3); serialports[0]->Putchar(reg_dl,true,true, 0xFFFFFFFF); // RTS off IO_WriteB(port+4,0x1); } } break; case 0x05: /* Write Character to PRINTER */ E_Exit("DOS:Unhandled call %02X",reg_ah); break; case 0x06: /* Direct Console Output / Input */ switch (reg_dl) { case 0xFF: /* Input */ { //Simulate DOS overhead for timing sensitive games //MM1 overhead(); //TODO Make this better according to standards if (!DOS_GetSTDINStatus()) { reg_al=0; CALLBACK_SZF(true); break; } Bit8u c;Bit16u n=1; DOS_ReadFile(STDIN,&c,&n); reg_al=c; CALLBACK_SZF(false); break; } default: { Bit8u c = reg_dl;Bit16u n = 1; dos.direct_output=true; DOS_WriteFile(STDOUT,&c,&n); dos.direct_output=false; reg_al=c; } break; }; break; case 0x07: /* Character Input, without echo */ { Bit8u c;Bit16u n=1; DOS_ReadFile (STDIN,&c,&n); reg_al=c; break; }; case 0x08: /* Direct Character Input, without echo (checks for breaks officially :)*/ { Bit8u c;Bit16u n=1; DOS_ReadFile (STDIN,&c,&n); reg_al=c; break; }; case 0x09: /* Write string to STDOUT */ { Bit8u c;Bit16u n=1; PhysPt buf=SegPhys(ds)+reg_dx; while ((c=mem_readb(buf++))!='$') { DOS_WriteFile(STDOUT,&c,&n); } reg_al=c; } break; case 0x0a: /* Buffered Input */ { //TODO ADD Break checkin in STDIN but can't care that much for it PhysPt data=SegPhys(ds)+reg_dx; Bit8u free=mem_readb(data); Bit8u read=0;Bit8u c;Bit16u n=1; if (!free) break; free--; for(;;) { DOS_ReadFile(STDIN,&c,&n); if (n == 0) // End of file E_Exit("DOS:0x0a:Redirected input reached EOF"); if (c == 10) // Line feed continue; if (c == 8) { // Backspace if (read) { //Something to backspace. // STDOUT treats backspace as non-destructive. DOS_WriteFile(STDOUT,&c,&n); c = ' '; DOS_WriteFile(STDOUT,&c,&n); c = 8; DOS_WriteFile(STDOUT,&c,&n); --read; } continue; } if (read == free && c != 13) { // Keyboard buffer full Bit8u bell = 7; DOS_WriteFile(STDOUT, &bell, &n); continue; } DOS_WriteFile(STDOUT,&c,&n); mem_writeb(data+read+2,c); if (c==13) break; read++; }; mem_writeb(data+1,read); break; }; case 0x0b: /* Get STDIN Status */ if (!DOS_GetSTDINStatus()) {reg_al=0x00;} else {reg_al=0xFF;} //Simulate some overhead for timing issues //Tankwar menu (needs maybe even more) overhead(); break; case 0x0c: /* Flush Buffer and read STDIN call */ { /* flush buffer if STDIN is CON */ Bit8u handle=RealHandle(STDIN); if (handle!=0xFF && Files[handle] && Files[handle]->IsName("CON")) { Bit8u c;Bit16u n; while (DOS_GetSTDINStatus()) { n=1; DOS_ReadFile(STDIN,&c,&n); } } switch (reg_al) { case 0x1: case 0x6: case 0x7: case 0x8: case 0xa: { Bit8u oldah=reg_ah; reg_ah=reg_al; DOS_21Handler(); reg_ah=oldah; } break; default: // LOG_ERROR("DOS:0C:Illegal Flush STDIN Buffer call %d",reg_al); reg_al=0; break; } } break; //TODO Find out the values for when reg_al!=0 //TODO Hope this doesn't do anything special case 0x0d: /* Disk Reset */ //Sure let's reset a virtual disk break; case 0x0e: /* Select Default Drive */ DOS_SetDefaultDrive(reg_dl); reg_al=DOS_DRIVES; break; case 0x0f: /* Open File using FCB */ if(DOS_FCBOpen(SegValue(ds),reg_dx)){ reg_al=0; }else{ reg_al=0xff; } LOG(LOG_FCB,LOG_NORMAL)("DOS:0x0f FCB-fileopen used, result:al=%d",reg_al); break; case 0x10: /* Close File using FCB */ if(DOS_FCBClose(SegValue(ds),reg_dx)){ reg_al=0; }else{ reg_al=0xff; } LOG(LOG_FCB,LOG_NORMAL)("DOS:0x10 FCB-fileclose used, result:al=%d",reg_al); break; case 0x11: /* Find First Matching File using FCB */ if(DOS_FCBFindFirst(SegValue(ds),reg_dx)) reg_al = 0x00; else reg_al = 0xFF; LOG(LOG_FCB,LOG_NORMAL)("DOS:0x11 FCB-FindFirst used, result:al=%d",reg_al); break; case 0x12: /* Find Next Matching File using FCB */ if(DOS_FCBFindNext(SegValue(ds),reg_dx)) reg_al = 0x00; else reg_al = 0xFF; LOG(LOG_FCB,LOG_NORMAL)("DOS:0x12 FCB-FindNext used, result:al=%d",reg_al); break; case 0x13: /* Delete File using FCB */ if (DOS_FCBDeleteFile(SegValue(ds),reg_dx)) reg_al = 0x00; else reg_al = 0xFF; LOG(LOG_FCB,LOG_NORMAL)("DOS:0x16 FCB-Delete used, result:al=%d",reg_al); break; case 0x14: /* Sequential read from FCB */ reg_al = DOS_FCBRead(SegValue(ds),reg_dx,0); LOG(LOG_FCB,LOG_NORMAL)("DOS:0x14 FCB-Read used, result:al=%d",reg_al); break; case 0x15: /* Sequential write to FCB */ reg_al=DOS_FCBWrite(SegValue(ds),reg_dx,0); LOG(LOG_FCB,LOG_NORMAL)("DOS:0x15 FCB-Write used, result:al=%d",reg_al); break; case 0x16: /* Create or truncate file using FCB */ if (DOS_FCBCreate(SegValue(ds),reg_dx)) reg_al = 0x00; else reg_al = 0xFF; LOG(LOG_FCB,LOG_NORMAL)("DOS:0x16 FCB-Create used, result:al=%d",reg_al); break; case 0x17: /* Rename file using FCB */ if (DOS_FCBRenameFile(SegValue(ds),reg_dx)) reg_al = 0x00; else reg_al = 0xFF; break; case 0x1b: /* Get allocation info for default drive */ if (!DOS_GetAllocationInfo(0,®_cx,®_al,®_dx)) reg_al=0xff; break; case 0x1c: /* Get allocation info for specific drive */ if (!DOS_GetAllocationInfo(reg_dl,®_cx,®_al,®_dx)) reg_al=0xff; break; case 0x21: /* Read random record from FCB */ { Bit16u toread=1; reg_al = DOS_FCBRandomRead(SegValue(ds),reg_dx,&toread,true); } LOG(LOG_FCB,LOG_NORMAL)("DOS:0x21 FCB-Random read used, result:al=%d",reg_al); break; case 0x22: /* Write random record to FCB */ { Bit16u towrite=1; reg_al=DOS_FCBRandomWrite(SegValue(ds),reg_dx,&towrite,true); } LOG(LOG_FCB,LOG_NORMAL)("DOS:0x22 FCB-Random write used, result:al=%d",reg_al); break; case 0x23: /* Get file size for FCB */ if (DOS_FCBGetFileSize(SegValue(ds),reg_dx)) reg_al = 0x00; else reg_al = 0xFF; break; case 0x24: /* Set Random Record number for FCB */ DOS_FCBSetRandomRecord(SegValue(ds),reg_dx); break; case 0x27: /* Random block read from FCB */ reg_al = DOS_FCBRandomRead(SegValue(ds),reg_dx,®_cx,false); LOG(LOG_FCB,LOG_NORMAL)("DOS:0x27 FCB-Random(block) read used, result:al=%d",reg_al); break; case 0x28: /* Random Block write to FCB */ reg_al=DOS_FCBRandomWrite(SegValue(ds),reg_dx,®_cx,false); LOG(LOG_FCB,LOG_NORMAL)("DOS:0x28 FCB-Random(block) write used, result:al=%d",reg_al); break; case 0x29: /* Parse filename into FCB */ { Bit8u difference; char string[1024]; MEM_StrCopy(SegPhys(ds)+reg_si,string,1023); // 1024 toasts the stack reg_al=FCB_Parsename(SegValue(es),reg_di,reg_al ,string, &difference); reg_si+=difference; } LOG(LOG_FCB,LOG_NORMAL)("DOS:29:FCB Parse Filename, result:al=%d",reg_al); break; case 0x19: /* Get current default drive */ reg_al=DOS_GetDefaultDrive(); break; case 0x1a: /* Set Disk Transfer Area Address */ dos.dta(RealMakeSeg(ds,reg_dx)); break; case 0x25: /* Set Interrupt Vector */ RealSetVec(reg_al,RealMakeSeg(ds,reg_dx)); break; case 0x26: /* Create new PSP */ DOS_NewPSP(reg_dx,DOS_PSP(dos.psp()).GetSize()); reg_al=0xf0; /* al destroyed */ break; case 0x2a: /* Get System Date */ { reg_ax=0; // get time CALLBACK_RunRealInt(0x1a); if(reg_al) DOS_AddDays(reg_al); int a = (14 - dos.date.month)/12; int y = dos.date.year - a; int m = dos.date.month + 12*a - 2; reg_al=(dos.date.day+y+(y/4)-(y/100)+(y/400)+(31*m)/12) % 7; reg_cx=dos.date.year; reg_dh=dos.date.month; reg_dl=dos.date.day; } break; case 0x2b: /* Set System Date */ if (reg_cx<1980) { reg_al=0xff;break;} if ((reg_dh>12) || (reg_dh==0)) { reg_al=0xff;break;} if (reg_dl==0) { reg_al=0xff;break;} if (reg_dl>DOS_DATE_months[reg_dh]) { if(!((reg_dh==2)&&(reg_cx%4 == 0)&&(reg_dl==29))) // february pass { reg_al=0xff;break; } } dos.date.year=reg_cx; dos.date.month=reg_dh; dos.date.day=reg_dl; reg_al=0; break; case 0x2c: { /* Get System Time */ reg_ax=0; // get time CALLBACK_RunRealInt(0x1a); if(reg_al) DOS_AddDays(reg_al); reg_ah=0x2c; Bitu ticks=((Bitu)reg_cx<<16)|reg_dx; if(time_start<=ticks) ticks-=time_start; Bitu time=(Bitu)((100.0/((double)PIT_TICK_RATE/65536.0)) * (double)ticks); reg_dl=(Bit8u)((Bitu)time % 100); // 1/100 seconds time/=100; reg_dh=(Bit8u)((Bitu)time % 60); // seconds time/=60; reg_cl=(Bit8u)((Bitu)time % 60); // minutes time/=60; reg_ch=(Bit8u)((Bitu)time % 24); // hours //Simulate DOS overhead for timing-sensitive games //Robomaze 2 overhead(); break; } case 0x2d: /* Set System Time */ LOG(LOG_DOSMISC,LOG_ERROR)("DOS:Set System Time not supported"); //Check input parameters nonetheless if( reg_ch > 23 || reg_cl > 59 || reg_dh > 59 || reg_dl > 99 ) reg_al = 0xff; else { //Allow time to be set to zero. Restore the orginal time for all other parameters. (QuickBasic) if (reg_cx == 0 && reg_dx == 0) {time_start = mem_readd(BIOS_TIMER);LOG_MSG("Warning: game messes with DOS time!");} else time_start = 0; reg_al = 0; } break; case 0x2e: /* Set Verify flag */ dos.verify=(reg_al==1); break; case 0x2f: /* Get Disk Transfer Area */ SegSet16(es,RealSeg(dos.dta())); reg_bx=RealOff(dos.dta()); break; case 0x30: /* Get DOS Version */ if (reg_al==0) reg_bh=0xFF; /* Fake Microsoft DOS */ if (reg_al==1) reg_bh=0x10; /* DOS is in HMA */ reg_al=dos.version.major; reg_ah=dos.version.minor; /* Serialnumber */ reg_bl=0x00; reg_cx=0x0000; break; case 0x31: /* Terminate and stay resident */ // Important: This service does not set the carry flag! DOS_ResizeMemory(dos.psp(),®_dx); DOS_Terminate(dos.psp(),true,reg_al); break; case 0x1f: /* Get drive parameter block for default drive */ case 0x32: /* Get drive parameter block for specific drive */ { /* Officially a dpb should be returned as well. The disk detection part is implemented */ Bit8u drive=reg_dl; if (!drive || reg_ah==0x1f) drive = DOS_GetDefaultDrive(); else drive--; if (Drives[drive]) { reg_al = 0x00; SegSet16(ds,dos.tables.dpb); reg_bx = drive;//Faking only the first entry (that is the driveletter) LOG(LOG_DOSMISC,LOG_ERROR)("Get drive parameter block."); } else { reg_al=0xff; } } break; case 0x33: /* Extended Break Checking */ switch (reg_al) { case 0:reg_dl=dos.breakcheck;break; /* Get the breakcheck flag */ case 1:dos.breakcheck=(reg_dl>0);break; /* Set the breakcheck flag */ case 2:{bool old=dos.breakcheck;dos.breakcheck=(reg_dl>0);reg_dl=old;}break; case 3: /* Get cpsw */ /* Fallthrough */ case 4: /* Set cpsw */ LOG(LOG_DOSMISC,LOG_ERROR)("Someone playing with cpsw %x",reg_ax); break; case 5:reg_dl=3;break;//TODO should be z /* Always boot from c: :) */ case 6: /* Get true version number */ reg_bl=dos.version.major; reg_bh=dos.version.minor; reg_dl=dos.version.revision; reg_dh=0x10; /* Dos in HMA */ break; default: LOG(LOG_DOSMISC,LOG_ERROR)("Weird 0x33 call %2X",reg_al); reg_al =0xff; break; } break; case 0x34: /* Get INDos Flag */ SegSet16(es,DOS_SDA_SEG); reg_bx=DOS_SDA_OFS + 0x01; break; case 0x35: /* Get interrupt vector */ reg_bx=real_readw(0,((Bit16u)reg_al)*4); SegSet16(es,real_readw(0,((Bit16u)reg_al)*4+2)); break; case 0x36: /* Get Free Disk Space */ { Bit16u bytes,clusters,free; Bit8u sectors; if (DOS_GetFreeDiskSpace(reg_dl,&bytes,§ors,&clusters,&free)) { reg_ax=sectors; reg_bx=free; reg_cx=bytes; reg_dx=clusters; } else { Bit8u drive=reg_dl; if (drive==0) drive=DOS_GetDefaultDrive(); else drive--; if (drive<2) { // floppy drive, non-present drivesdisks issue floppy check through int24 // (critical error handler); needed for Mixed up Mother Goose (hook) // CALLBACK_RunRealInt(0x24); } reg_ax=0xffff; // invalid drive specified } } break; case 0x37: /* Get/Set Switch char Get/Set Availdev thing */ //TODO Give errors for these functions to see if anyone actually uses this shit- switch (reg_al) { case 0: reg_al=0;reg_dl=0x2f;break; /* always return '/' like dos 5.0+ */ case 1: reg_al=0;break; case 2: reg_al=0;reg_dl=0x2f;break; case 3: reg_al=0;break; }; LOG(LOG_MISC,LOG_ERROR)("DOS:0x37:Call for not supported switchchar"); break; case 0x38: /* Set Country Code */ if (reg_al==0) { /* Get country specidic information */ PhysPt dest = SegPhys(ds)+reg_dx; MEM_BlockWrite(dest,dos.tables.country,0x18); reg_ax = reg_bx = 0x01; CALLBACK_SCF(false); break; } else { /* Set country code */ LOG(LOG_MISC,LOG_ERROR)("DOS:Setting country code not supported"); } CALLBACK_SCF(true); break; case 0x39: /* MKDIR Create directory */ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); if (DOS_MakeDir(name1)) { reg_ax=0x05; /* ax destroyed */ CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; CALLBACK_SCF(true); } break; case 0x3a: /* RMDIR Remove directory */ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); if (DOS_RemoveDir(name1)) { reg_ax=0x05; /* ax destroyed */ CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; CALLBACK_SCF(true); LOG(LOG_MISC,LOG_NORMAL)("Remove dir failed on %s with error %X",name1,dos.errorcode); } break; case 0x3b: /* CHDIR Set current directory */ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); if (DOS_ChangeDir(name1)) { reg_ax=0x00; /* ax destroyed */ CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; CALLBACK_SCF(true); } break; case 0x3c: /* CREATE Create of truncate file */ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); if (DOS_CreateFile(name1,reg_cx,®_ax)) { CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; CALLBACK_SCF(true); } break; case 0x3d: /* OPEN Open existing file */ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); if (DOS_OpenFile(name1,reg_al,®_ax)) { CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; CALLBACK_SCF(true); } break; case 0x3e: /* CLOSE Close file */ if (DOS_CloseFile(reg_bx)) { // reg_al=0x01; /* al destroyed. Refcount */ CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; CALLBACK_SCF(true); } break; case 0x3f: /* READ Read from file or device */ { Bit16u toread=reg_cx; dos.echo=true; if (DOS_ReadFile(reg_bx,dos_copybuf,&toread)) { MEM_BlockWrite(SegPhys(ds)+reg_dx,dos_copybuf,toread); reg_ax=toread; CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; CALLBACK_SCF(true); } modify_cycles(reg_ax); dos.echo=false; break; } case 0x40: /* WRITE Write to file or device */ { Bit16u towrite=reg_cx; MEM_BlockRead(SegPhys(ds)+reg_dx,dos_copybuf,towrite); if (DOS_WriteFile(reg_bx,dos_copybuf,&towrite)) { reg_ax=towrite; CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; CALLBACK_SCF(true); } modify_cycles(reg_ax); break; }; case 0x41: /* UNLINK Delete file */ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); if (DOS_UnlinkFile(name1)) { CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; CALLBACK_SCF(true); } break; case 0x42: /* LSEEK Set current file position */ { Bit32u pos=(reg_cx<<16) + reg_dx; if (DOS_SeekFile(reg_bx,&pos,reg_al)) { reg_dx=(Bit16u)(pos >> 16); reg_ax=(Bit16u)(pos & 0xFFFF); CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; CALLBACK_SCF(true); } break; }
void DOS_Shell::InputCommand(char * line) { Bitu size=CMD_MAXLINE-2; //lastcharacter+0 Bit8u c;Bit16u n=1; Bitu str_len=0;Bitu str_index=0; Bit16u len=0; bool current_hist=false; // current command stored in history? line[0] = '\0'; std::list<std::string>::iterator it_history = l_history.begin(), it_completion = l_completion.begin(); while (size) { dos.echo=false; while(!DOS_ReadFile(input_handle,&c,&n)) { Bit16u dummy; DOS_CloseFile(input_handle); DOS_OpenFile("con",2,&dummy); LOG(LOG_MISC,LOG_ERROR)("Reopening the input handle.This is a bug!"); } if (!n) { size=0; //Kill the while loop continue; } switch (c) { case 0x00: /* Extended Keys */ { DOS_ReadFile(input_handle,&c,&n); switch (c) { case 0x3d: /* F3 */ if (!l_history.size()) break; it_history = l_history.begin(); if (it_history != l_history.end() && it_history->length() > str_len) { const char *reader = &(it_history->c_str())[str_len]; while ((c = *reader++)) { line[str_index ++] = c; DOS_WriteFile(STDOUT,&c,&n); } str_len = str_index = (Bitu)it_history->length(); size = CMD_MAXLINE - str_index - 2; line[str_len] = 0; } break; case 0x4B: /* LEFT */ if (str_index) { outc(8); str_index --; } break; case 0x4D: /* RIGHT */ if (str_index < str_len) { outc(line[str_index++]); } break; case 0x47: /* HOME */ while (str_index) { outc(8); str_index--; } break; case 0x4F: /* END */ while (str_index < str_len) { outc(line[str_index++]); } break; case 0x48: /* UP */ if (l_history.empty() || it_history == l_history.end()) break; // store current command in history if we are at beginning if (it_history == l_history.begin() && !current_hist) { current_hist=true; l_history.push_front(line); } for (;str_index>0; str_index--) { // removes all characters outc(8); outc(' '); outc(8); } strcpy(line, it_history->c_str()); len = (Bit16u)it_history->length(); str_len = str_index = len; size = CMD_MAXLINE - str_index - 2; DOS_WriteFile(STDOUT, (Bit8u *)line, &len); it_history ++; break; case 0x50: /* DOWN */ if (l_history.empty() || it_history == l_history.begin()) break; // not very nice but works .. it_history --; if (it_history == l_history.begin()) { // no previous commands in history it_history ++; // remove current command from history if (current_hist) { current_hist=false; l_history.pop_front(); } break; } else it_history --; for (;str_index>0; str_index--) { // removes all characters outc(8); outc(' '); outc(8); } strcpy(line, it_history->c_str()); len = (Bit16u)it_history->length(); str_len = str_index = len; size = CMD_MAXLINE - str_index - 2; DOS_WriteFile(STDOUT, (Bit8u *)line, &len); it_history ++; break; case 0x53:/* DELETE */ { if(str_index>=str_len) break; Bit16u a=str_len-str_index-1; Bit8u* text=reinterpret_cast<Bit8u*>(&line[str_index+1]); DOS_WriteFile(STDOUT,text,&a);//write buffer to screen outc(' ');outc(8); for(Bitu i=str_index;i<str_len-1;i++) { line[i]=line[i+1]; outc(8); } line[--str_len]=0; size++; } break; default: break; } }; break; case 0x08: /* BackSpace */ if (str_index) { outc(8); Bit32u str_remain=str_len - str_index; size++; if (str_remain) { memmove(&line[str_index-1],&line[str_index],str_remain); line[--str_len]=0; str_index --; /* Go back to redraw */ for (Bit16u i=str_index; i < str_len; i++) outc(line[i]); } else { line[--str_index] = '\0'; str_len--; } outc(' '); outc(8); // moves the cursor left while (str_remain--) outc(8); } if (l_completion.size()) l_completion.clear(); break; case 0x0a: /* New Line not handled */ /* Don't care */ break; case 0x0d: /* Return */ outc('\n'); size=0; //Kill the while loop break; case'\t': { if (l_completion.size()) { it_completion ++; if (it_completion == l_completion.end()) it_completion = l_completion.begin(); } else { // build new completion list // Lines starting with CD will only get directories in the list bool dir_only = (strncasecmp(line,"CD ",3)==0); // get completion mask char *p_completion_start = strrchr(line, ' '); if (p_completion_start) { p_completion_start ++; completion_index = (Bit16u)(str_len - strlen(p_completion_start)); } else { p_completion_start = line; completion_index = 0; } char *path; if ((path = strrchr(line+completion_index,'\\'))) completion_index = (Bit16u)(path-line+1); if ((path = strrchr(line+completion_index,'/'))) completion_index = (Bit16u)(path-line+1); // build the completion list char mask[DOS_PATHLENGTH]; if (p_completion_start) { strcpy(mask, p_completion_start); char* dot_pos=strrchr(mask,'.'); char* bs_pos=strrchr(mask,'\\'); char* fs_pos=strrchr(mask,'/'); char* cl_pos=strrchr(mask,':'); // not perfect when line already contains wildcards, but works if ((dot_pos-bs_pos>0) && (dot_pos-fs_pos>0) && (dot_pos-cl_pos>0)) strcat(mask, "*"); else strcat(mask, "*.*"); } else { strcpy(mask, "*.*"); } RealPt save_dta=dos.dta(); dos.dta(dos.tables.tempdta); bool res = DOS_FindFirst(mask, 0xffff & ~DOS_ATTR_VOLUME); if (!res) { dos.dta(save_dta); break; // TODO: beep } DOS_DTA dta(dos.dta()); char name[DOS_NAMELENGTH_ASCII];Bit32u sz;Bit16u date;Bit16u time;Bit8u att; std::list<std::string> executable; while (res) { dta.GetResult(name,sz,date,time,att); // add result to completion list char *ext; // file extension if (strcmp(name, ".") && strcmp(name, "..")) { if (dir_only) { //Handle the dir only case different (line starts with cd) if(att & DOS_ATTR_DIRECTORY) l_completion.push_back(name); } else { ext = strrchr(name, '.'); if (ext && (strcmp(ext, ".BAT") == 0 || strcmp(ext, ".COM") == 0 || strcmp(ext, ".EXE") == 0)) // we add executables to the a seperate list and place that list infront of the normal files executable.push_front(name); else l_completion.push_back(name); } } res=DOS_FindNext(); } /* Add excutable list to front of completion list. */ std::copy(executable.begin(),executable.end(),std::front_inserter(l_completion)); it_completion = l_completion.begin(); dos.dta(save_dta); } if (l_completion.size() && it_completion->length()) { for (;str_index > completion_index; str_index--) { // removes all characters outc(8); outc(' '); outc(8); } strcpy(&line[completion_index], it_completion->c_str()); len = (Bit16u)it_completion->length(); str_len = str_index = completion_index + len; size = CMD_MAXLINE - str_index - 2; DOS_WriteFile(STDOUT, (Bit8u *)it_completion->c_str(), &len); } } break; case 0x1b: /* ESC */ //write a backslash and return to the next line outc('\\'); outc('\n'); *line = 0; // reset the line. if (l_completion.size()) l_completion.clear(); //reset the completion list. this->InputCommand(line); //Get the NEW line. size = 0; // stop the next loop str_len = 0; // prevent multiple adds of the same line break; default: if (l_completion.size()) l_completion.clear(); if(str_index < str_len && true) { //mem_readb(BIOS_KEYBOARD_FLAGS1)&0x80) dev_con.h ? outc(' ');//move cursor one to the right. Bit16u a = str_len - str_index; Bit8u* text=reinterpret_cast<Bit8u*>(&line[str_index]); DOS_WriteFile(STDOUT,text,&a);//write buffer to screen outc(8);//undo the cursor the right. for(Bitu i=str_len;i>str_index;i--) { line[i]=line[i-1]; //move internal buffer outc(8); //move cursor back (from write buffer to screen) } line[++str_len]=0;//new end (as the internal buffer moved one place to the right size--; }; line[str_index]=c; str_index ++; if (str_index > str_len){ line[str_index] = '\0'; str_len++; size--; } DOS_WriteFile(STDOUT,&c,&n); break; } } if (!str_len) return; str_len++; // remove current command from history if it's there if (current_hist) { current_hist=false; l_history.pop_front(); } // add command line to history l_history.push_front(line); it_history = l_history.begin(); if (l_completion.size()) l_completion.clear(); }
bool BatchFile::ReadLine(char * line) { //Open the batchfile and seek to stored postion if (!DOS_OpenFile(filename.c_str(),128,&file_handle)) { LOG(LOG_MISC,LOG_ERROR)("ReadLine Can't open BatchFile %s",filename.c_str()); delete this; return false; } DOS_SeekFile(file_handle,&(this->location),DOS_SEEK_SET); Bit8u c=0;Bit16u n=1; char temp[CMD_MAXLINE]; emptyline: char * cmd_write=temp; do { n=1; DOS_ReadFile(file_handle,&c,&n); if (n>0) { /* Why are we filtering this ? * Exclusion list: tab for batch files * escape for ansi * backspace for alien odyssey */ if (c>31 || c==0x1b || c=='\t' || c==8) *cmd_write++=c; } } while (c!='\n' && n); *cmd_write=0; if (!n && cmd_write==temp) { //Close file and delete bat file DOS_CloseFile(file_handle); delete this; return false; } if (!strlen(temp)) goto emptyline; if (temp[0]==':') goto emptyline; /* Now parse the line read from the bat file for % stuff */ cmd_write=line; char * cmd_read=temp; while (*cmd_read) { if (*cmd_read=='%') { cmd_read++; if (cmd_read[0] == '%') { cmd_read++; *cmd_write++='%'; continue; } if (cmd_read[0] == '0') { /* Handle %0 */ const char *file_name = cmd->GetFileName(); cmd_read++; strcpy(cmd_write,file_name); cmd_write+=strlen(file_name); continue; } char next = cmd_read[0]; if(next > '0' && next <= '9') { /* Handle %1 %2 .. %9 */ cmd_read++; //Progress reader next -= '0'; if (cmd->GetCount()<(unsigned int)next) continue; std::string word; if (!cmd->FindCommand(next,word)) continue; strcpy(cmd_write,word.c_str()); cmd_write+=strlen(word.c_str()); continue; } else { /* Not a command line number has to be an environment */ char * first=strchr(cmd_read,'%'); /* No env afterall.Somewhat of a hack though as %% and % aren't handled consistent in dosbox. Maybe echo needs to parse % and %% as well. */ if (!first) {*cmd_write++ = '%';continue;} *first++ = 0; std::string env; if (shell->GetEnvStr(cmd_read,env)) { const char * equals=strchr(env.c_str(),'='); if (!equals) continue; equals++; strcpy(cmd_write,equals); cmd_write+=strlen(equals); } cmd_read=first; } } else { *cmd_write++=*cmd_read++; } } *cmd_write=0; //Store current location and close bat file this->location = 0; DOS_SeekFile(file_handle,&(this->location),DOS_SEEK_CUR); DOS_CloseFile(file_handle); return true; }
static Bitu DOS_21Handler(void) { if (((reg_ah != 0x50) && (reg_ah != 0x51) && (reg_ah != 0x62) && (reg_ah != 0x64)) && (reg_ah < 0x6c)) if (dos.psp() != 0) // No program loaded yet? { DOS_PSP psp(dos.psp()); psp.SetStack(SegOff2dWord(SegValue(ss), reg_sp-18)); psp.FindFreeFileEntry(); } char name1[DOSNAMEBUF+2+DOS_NAMELENGTH_ASCII]; char name2[DOSNAMEBUF+2+DOS_NAMELENGTH_ASCII]; switch (reg_ah) { case 0x00: // Terminate Program DOS_Terminate(Mem_Lodsw(SegPhys(ss)+reg_sp+2), false, 0); break; case 0x01: // Read character from STDIN, with echo { Bit8u c; Bit16u n = 1; dos.echo = true; DOS_ReadFile(STDIN, &c, &n); reg_al = c; dos.echo = false; } break; case 0x02: // Write character to STDOUT { Bit8u c = reg_dl; Bit16u n = 1; DOS_WriteFile(STDOUT, &c, &n); // Not in the official specs, but happens nonetheless. (last written character) reg_al = c; // reg_al=(c==9)?0x20:c; //Officially: tab to spaces } break; case 0x03: // Read character from STDAUX { Bit16u port = Mem_Lodsw(0x40, 0); if (port != 0 && serialPorts[0]) serialPorts[0]->Getchar(®_al); } break; case 0x04: // Write Character to STDAUX { Bit16u port = Mem_Lodsw(0x40, 0); if (port != 0 && serialPorts[0]) serialPorts[0]->Putchar(reg_dl); } break; case 0x05: // Write Character to PRINTER parallelPorts[0]->Putchar(reg_dl); break; case 0x06: // Direct Console Output/Input if (reg_dl == 0xff) // Input { // TODO Make this better according to standards if (!DOS_GetSTDINStatus()) { reg_al = 0; CALLBACK_SZF(true); break; } Bit8u c; Bit16u n = 1; DOS_ReadFile(STDIN, &c, &n); reg_al = c; CALLBACK_SZF(false); } else // Ouput { Bit8u c = reg_dl; Bit16u n = 1; DOS_WriteFile(STDOUT, &c, &n); reg_al = reg_dl; } break; case 0x07: // Character Input, without echo case 0x08: // Direct Character Input, without echo (checks for breaks officially :) { Bit8u c; Bit16u n = 1; DOS_ReadFile (STDIN, &c, &n); reg_al = c; } break; case 0x09: // Write string to STDOUT { Bit8u c; Bit16u n = 1; PhysPt buf = SegPhys(ds)+reg_dx; while ((c = Mem_Lodsb(buf++)) != '$') DOS_WriteFile(STDOUT, &c, &n); } break; case 0x0a: // Buffered Input { // TODO ADD Break checkin in STDIN but can't care that much for it PhysPt data = SegPhys(ds)+reg_dx; Bit8u free = Mem_Lodsb(data); if (!free) break; Bit8u read = 0; Bit8u c; Bit16u n = 1; for(;;) { DOS_ReadFile(STDIN, &c ,&n); if (c == 8) { // Backspace if (read) { // Something to backspace. DOS_WriteFile(STDOUT, &c, &n); // STDOUT treats backspace as non-destructive. c = ' '; DOS_WriteFile(STDOUT, &c, &n); c = 8; DOS_WriteFile(STDOUT, &c, &n); --read; } continue; } if (read >= free) // Keyboard buffer full { Beep(1750, 300); continue; } DOS_WriteFile(STDOUT, &c, &n); Mem_Stosb(data+read+2, c); if (c == 13) break; read++; } Mem_Stosb(data+1, read); } break; case 0x0b: // Get STDIN Status reg_al = DOS_GetSTDINStatus() ? 0xff : 0x00; break; case 0x0c: // Flush Buffer and read STDIN call { Bit8u handle = RealHandle(STDIN); if (handle != 0xFF && Files[handle] && Files[handle]->IsName("CON")) // flush buffer if STDIN is CON { Bit8u c; Bit16u n; while (DOS_GetSTDINStatus()) { n = 1; DOS_ReadFile(STDIN, &c, &n); } } if (reg_al == 0x1 || reg_al == 0x6 || reg_al == 0x7 || reg_al == 0x8 || reg_al == 0xa) { Bit8u oldah = reg_ah; reg_ah = reg_al; DOS_21Handler(); reg_ah = oldah; } else reg_al = 0; } break; case 0x0d: // Disk Reset break; case 0x0e: // Select Default Drive DOS_SetDefaultDrive(reg_dl); reg_al = DOS_DRIVES; break; case 0x0f: // Open File using FCB reg_al = DOS_FCBOpen(SegValue(ds), reg_dx) ? 0 : 0xFF; break; case 0x10: // Close File using FCB reg_al = DOS_FCBClose(SegValue(ds), reg_dx) ? 0 : 0xFF; break; case 0x11: // Find First Matching File using FCB reg_al = DOS_FCBFindFirst(SegValue(ds), reg_dx) ? 0 : 0xFF; // No test for C:\COMMAND.COM! break; case 0x12: // Find Next Matching File using FCB reg_al = DOS_FCBFindNext(SegValue(ds), reg_dx) ? 0 : 0xFF; break; case 0x13: // Delete File using FCB reg_al = DOS_FCBDeleteFile(SegValue(ds), reg_dx) ? 0 : 0xFF; break; case 0x14: // Sequential read from FCB reg_al = DOS_FCBRead(SegValue(ds), reg_dx, 0); break; case 0x15: // Sequential write to FCB reg_al = DOS_FCBWrite(SegValue(ds), reg_dx, 0); break; case 0x16: // Create or truncate file using FCB reg_al = DOS_FCBCreate(SegValue(ds), reg_dx) ? 0 : 0xFF; break; case 0x17: // Rename file using FCB reg_al = DOS_FCBRenameFile(SegValue(ds), reg_dx) ? 0 : 0xFF; break; case 0x1b: // Get allocation info for default drive if (!DOS_GetAllocationInfo(0, ®_cx, ®_al, ®_dx)) reg_al = 0xFF; break; case 0x1c: // Get allocation info for specific drive if (!DOS_GetAllocationInfo(reg_dl, ®_cx, ®_al, ®_dx)) reg_al = 0xFF; break; case 0x21: // Read random record from FCB reg_al = DOS_FCBRandomRead(SegValue(ds), reg_dx, 1, true); break; case 0x22: // Write random record to FCB reg_al = DOS_FCBRandomWrite(SegValue(ds), reg_dx, 1, true); break; case 0x23: // Get file size for FCB reg_al = DOS_FCBGetFileSize(SegValue(ds), reg_dx) ? 0 : 0xFF; break; case 0x24: // Set Random Record number for FCB DOS_FCBSetRandomRecord(SegValue(ds), reg_dx); break; case 0x27: // Random block read from FCB reg_al = DOS_FCBRandomRead(SegValue(ds), reg_dx, reg_cx, false); break; case 0x28: // Random Block write to FCB reg_al = DOS_FCBRandomWrite(SegValue(ds), reg_dx, reg_cx, false); break; case 0x29: // Parse filename into FCB { Bit8u difference; char string[1024]; Mem_StrnCopyFrom(string, SegPhys(ds)+reg_si, 1023); // 1024 toasts the stack reg_al = FCB_Parsename(SegValue(es), reg_di, reg_al, string, &difference); reg_si += difference; } break; case 0x19: // Get current default drive reg_al = DOS_GetDefaultDrive(); break; case 0x1a: // Set Disk Transfer Area Address dos.dta(RealMakeSeg(ds, reg_dx)); break; case 0x25: // Set Interrupt Vector RealSetVec(reg_al, RealMakeSeg(ds, reg_dx)); break; case 0x26: // Create new PSP DOS_NewPSP(reg_dx, DOS_PSP(dos.psp()).GetSize()); break; case 0x2a: // Get System Date { _SYSTEMTIME systime; // Return the Windows localdate GetLocalTime(&systime); reg_al = (Bit8u)systime.wDayOfWeek; // NB Sunday = 0, despite of MSDN documentation reg_cx = systime.wYear; reg_dx = (systime.wMonth<<8)+systime.wDay; } break; case 0x2b: // Set System Date (we don't!) reg_al = 0xff; daysInMonth[2] = reg_cx&3 ? 28 : 29; // Year is from 1980 to 2099, it is this simple if (reg_cx >= 1980 && reg_cx <= 2099) if (reg_dh > 0 && reg_dh <= 12) if (reg_dl > 0 && reg_dl <= daysInMonth[reg_dh]) reg_al = 0; // Date is valid, fake set break; case 0x2c: // Get System Time { _SYSTEMTIME systime; // Return the Windows localtime GetLocalTime(&systime); reg_cx = (systime.wHour<<8) + systime.wMinute; reg_dx = (systime.wSecond<<8) + systime.wMilliseconds/10; } break; case 0x2d: // Set System Time (we don't!) if (reg_ch < 24 && reg_cl < 60 && reg_dh < 60 && reg_dl < 100) reg_al = 0; // Time is valid, fake set else reg_al = 0xff; break; case 0x2e: // Set Verify flag dos.verify = (reg_al == 1); break; case 0x2f: // Get Disk Transfer Area SegSet16(es, RealSeg(dos.dta())); reg_bx = RealOff(dos.dta()); break; case 0x30: // Get DOS Version if (reg_al == 0) reg_bh = 0xFF; // Fake Microsoft DOS else if (reg_al == 1) reg_bh = 0; // DOS is NOT in HMA reg_al = dos.version.major; reg_ah = dos.version.minor; reg_bl = 0; // Serialnumber reg_cx = 0; break; case 0x31: // Terminate and stay resident // Important: This service does not set the carry flag! DOS_ResizeMemory(dos.psp(), ®_dx); DOS_Terminate(dos.psp(), true, reg_al); break; case 0x1f: // Get drive parameter block for default drive case 0x32: // Get drive parameter block for specific drive { // Officially a dpb should be returned as well. The disk detection part is implemented Bit8u drive = reg_dl; if (!drive || reg_ah == 0x1f) drive = DOS_GetDefaultDrive(); else drive--; if (Drives[drive]) { reg_al = 0; SegSet16(ds, dos.tables.dpb); reg_bx = drive; // Faking only the first entry (that is the driveletter) } else reg_al = 0xff; } break; case 0x33: // Extended Break Checking switch (reg_al) { case 0: // Get the breakcheck flag reg_dl = dos.breakcheck; break; case 1: // Set the breakcheck flag dos.breakcheck = (reg_dl > 0); break; case 2: { bool old = dos.breakcheck; dos.breakcheck = (reg_dl > 0); reg_dl = old; } break; case 3: // Get cpsw case 4: // Set cpsw, both not used really break; case 5: reg_dl = 3; // Boot drive = C: break; case 6: // Get true version number reg_bx = (dos.version.minor<<8) + dos.version.major; reg_dx = 0x1000; // Dos in ROM break; default: reg_al = 0xff; } break; case 0x34: // Get address of INDos Flag SegSet16(es, DOS_SDA_SEG); reg_bx = DOS_SDA_OFS + 0x01; break; case 0x35: // Get interrupt vector reg_bx = Mem_Lodsw(0, ((Bit16u)reg_al)*4); SegSet16(es, Mem_Lodsw(0, ((Bit16u)reg_al)*4+2)); break; case 0x36: // Get Free Disk Space { Bit16u bytes, clusters, free; Bit8u sectors; if (DOS_GetFreeDiskSpace(reg_dl, &bytes, §ors, &clusters, &free)) { reg_ax = sectors; reg_bx = free; reg_cx = bytes; reg_dx = clusters; } else reg_ax = 0xffff; // invalid drive specified } break; case 0x37: // Get/Set Switch char Get/Set Availdev thing switch (reg_al) { case 0: reg_dl = 0x2f; // Always return '/' like dos 5.0+ break; case 1: reg_al = 0; break; case 2: reg_al = 0; reg_dl = 0x2f; break; case 3: reg_al = 0; break; } break; case 0x38: // Get/set Country Code if (reg_al == 0) // Get country specidic information { PhysPt dest = SegPhys(ds)+reg_dx; Mem_CopyTo(dest, dos.tables.country, 0x18); reg_ax = reg_bx = 1; CALLBACK_SCF(false); } else // Set country code CALLBACK_SCF(true); break; case 0x39: // MKDIR Create directory Mem_StrnCopyFrom(name1, SegPhys(ds)+reg_dx, DOSNAMEBUF); rSpTrim(name1); if (DOS_MakeDir(name1)) { reg_ax = 0xffff; // AX destroyed CALLBACK_SCF(false); } else { reg_ax = dos.errorcode; CALLBACK_SCF(true); } break; case 0x3a: // RMDIR Remove directory Mem_StrnCopyFrom(name1, SegPhys(ds)+reg_dx, DOSNAMEBUF); rSpTrim(name1); if (DOS_RemoveDir(name1)) { reg_ax = 0xffff; // AX destroyed CALLBACK_SCF(false); } else { reg_ax = dos.errorcode; CALLBACK_SCF(true); } break; case 0x3b: // CHDIR Set current directory Mem_StrnCopyFrom(name1, SegPhys(ds)+reg_dx, DOSNAMEBUF); rSpTrim(name1); if (DOS_ChangeDir(name1)) { reg_ax = 0xff00; // AX destroyed CALLBACK_SCF(false); } else { reg_ax = dos.errorcode; CALLBACK_SCF(true); } break; case 0x3c: // CREATE Create or truncate file Mem_StrnCopyFrom(name1, SegPhys(ds)+reg_dx, DOSNAMEBUF); rSpTrim(name1); if (DOS_CreateFile(name1, reg_cx, ®_ax)) CALLBACK_SCF(false); else { reg_ax = dos.errorcode; CALLBACK_SCF(true); } break; case 0x3d: // OPEN Open existing file { Mem_StrnCopyFrom(name1, SegPhys(ds)+reg_dx, DOSNAMEBUF); rSpTrim(name1); if (DOS_OpenFile(name1, reg_al, ®_ax)) CALLBACK_SCF(false); else { reg_ax = dos.errorcode; CALLBACK_SCF(true); } break; } case 0x3e: // CLOSE Close file if (DOS_CloseFile(reg_bx)) CALLBACK_SCF(false); else { reg_ax = dos.errorcode; CALLBACK_SCF(true); } break; case 0x3f: // READ Read from file or device { Bit16u toread = reg_cx; dos.echo = true; if (DOS_ReadFile(reg_bx, dos_copybuf, &toread)) { Mem_CopyTo(SegPhys(ds)+reg_dx, dos_copybuf, toread); reg_ax = toread; CALLBACK_SCF(false); } else { reg_ax = dos.errorcode; CALLBACK_SCF(true); } dos.echo = false; } break; case 0x40: // WRITE Write to file or device { Bit16u towrite = reg_cx; Mem_CopyFrom(SegPhys(ds)+reg_dx, dos_copybuf, towrite); if (DOS_WriteFile(reg_bx, dos_copybuf, &towrite)) { reg_ax = towrite; CALLBACK_SCF(false); } else { reg_ax = dos.errorcode; CALLBACK_SCF(true); } } break; case 0x41: // UNLINK Delete file Mem_StrnCopyFrom(name1, SegPhys(ds)+reg_dx, DOSNAMEBUF); rSpTrim(name1); if (DOS_UnlinkFile(name1)) CALLBACK_SCF(false); else { reg_ax = dos.errorcode; CALLBACK_SCF(true); } break; case 0x42: // LSEEK Set current file position { Bit32u pos = (reg_cx<<16) + reg_dx; if (DOS_SeekFile(reg_bx, &pos, reg_al)) { reg_dx = (Bit16u)(pos >> 16); reg_ax = (Bit16u)(pos & 0xFFFF); CALLBACK_SCF(false); } else { reg_ax = dos.errorcode; CALLBACK_SCF(true); } }
void DOS_Shell::CMD_COPY(char * args) { HELP("COPY"); static char defaulttarget[] = "."; StripSpaces(args); /* Command uses dta so set it to our internal dta */ RealPt save_dta=dos.dta(); dos.dta(dos.tables.tempdta); DOS_DTA dta(dos.dta()); Bit32u size;Bit16u date;Bit16u time;Bit8u attr; char name[DOS_NAMELENGTH_ASCII], lname[LFN_NAMELENGTH+1]; std::vector<copysource> sources; // ignore /b and /t switches: always copy binary while(ScanCMDBool(args,"B")) ; while(ScanCMDBool(args,"T")) ; //Shouldn't this be A ? while(ScanCMDBool(args,"A")) ; ScanCMDBool(args,"Y"); ScanCMDBool(args,"-Y"); ScanCMDBool(args,"V"); char * rem=ScanCMDRemain(args); if (rem) { WriteOut(MSG_Get("SHELL_ILLEGAL_SWITCH"),rem); dos.dta(save_dta); return; } // Gather all sources (extension to copy more then 1 file specified at command line) // Concatenating files go as follows: All parts except for the last bear the concat flag. // This construction allows them to be counted (only the non concat set) char q[]="\""; char* source_p = NULL; char source_x[DOS_PATHLENGTH+CROSS_LEN]; while ( (source_p = StripArg(args)) && *source_p ) { do { char* plus = strchr(source_p,'+'); // If StripWord() previously cut at a space before a plus then // set concatenate flag on last source and remove leading plus. if (plus == source_p && sources.size()) { sources[sources.size()-1].concat = true; // If spaces also followed plus then item is only a plus. if (strlen(++source_p)==0) break; plus = strchr(source_p,'+'); } if (plus) *plus++ = 0; safe_strncpy(source_x,source_p,CROSS_LEN); bool has_drive_spec = false; size_t source_x_len = strlen(source_x); if (source_x_len>0) { if (source_x[source_x_len-1]==':') has_drive_spec = true; } if (!has_drive_spec && !strpbrk(source_p,"*?") ) { //doubt that fu*\*.* is valid char spath[DOS_PATHLENGTH]; if (DOS_GetSFNPath(source_p,spath,false) && DOS_FindFirst(spath,0xffff & ~DOS_ATTR_VOLUME)) { dta.GetResult(name,lname,size,date,time,attr); if (attr & DOS_ATTR_DIRECTORY) strcat(source_x,"\\*.*"); } } sources.push_back(copysource(source_x,(plus)?true:false)); source_p = plus; } while(source_p && *source_p); } // At least one source has to be there if (!sources.size() || !sources[0].filename.size()) { WriteOut(MSG_Get("SHELL_MISSING_PARAMETER")); dos.dta(save_dta); return; }; copysource target; // If more then one object exists and last target is not part of a // concat sequence then make it the target. if(sources.size()>1 && !sources[sources.size()-2].concat){ target = sources.back(); sources.pop_back(); } //If no target => default target with concat flag true to detect a+b+c if(target.filename.size() == 0) target = copysource(defaulttarget,true); copysource oldsource; copysource source; Bit32u count = 0; while(sources.size()) { /* Get next source item and keep track of old source for concat start end */ oldsource = source; source = sources[0]; sources.erase(sources.begin()); //Skip first file if doing a+b+c. Set target to first file if(!oldsource.concat && source.concat && target.concat) { target = source; continue; } /* Make a full path in the args */ char pathSourcePre[DOS_PATHLENGTH], pathSource[DOS_PATHLENGTH+2]; char pathTarget[DOS_PATHLENGTH]; if (!DOS_Canonicalize(const_cast<char*>(source.filename.c_str()),pathSourcePre)) { WriteOut(MSG_Get("SHELL_ILLEGAL_PATH")); dos.dta(save_dta); return; } strcpy(pathSource,pathSourcePre); if (uselfn) sprintf(pathSource,"\"%s\"",pathSourcePre); // cut search pattern char* pos = strrchr(pathSource,'\\'); if (pos) *(pos+1) = 0; if (!DOS_Canonicalize(const_cast<char*>(target.filename.c_str()),pathTarget)) { WriteOut(MSG_Get("SHELL_ILLEGAL_PATH")); dos.dta(save_dta); return; } char* temp = strstr(pathTarget,"*.*"); if(temp) *temp = 0;//strip off *.* from target // add '\\' if target is a directory bool target_is_file = true; if (pathTarget[strlen(pathTarget)-1]!='\\') { if (DOS_FindFirst(pathTarget,0xffff & ~DOS_ATTR_VOLUME)) { dta.GetResult(name,lname,size,date,time,attr); if (attr & DOS_ATTR_DIRECTORY) { strcat(pathTarget,"\\"); target_is_file = false; } } } else target_is_file = false; //Find first sourcefile char sPath[DOS_PATHLENGTH]; bool ret = DOS_GetSFNPath(source.filename.c_str(),sPath,false) && DOS_FindFirst(const_cast<char*>(sPath),0xffff & ~DOS_ATTR_VOLUME); if (!ret) { WriteOut(MSG_Get("SHELL_CMD_FILE_NOT_FOUND"),const_cast<char*>(source.filename.c_str())); dos.dta(save_dta); return; } Bit16u sourceHandle,targetHandle; char nameTarget[DOS_PATHLENGTH]; char nameSource[DOS_PATHLENGTH]; bool second_file_of_current_source = false; while (ret) { dta.GetResult(name,lname,size,date,time,attr); if ((attr & DOS_ATTR_DIRECTORY)==0) { strcpy(nameSource,pathSource); strcat(nameSource,name); // Open Source if (DOS_OpenFile(nameSource,0,&sourceHandle)) { // Create Target or open it if in concat mode strcpy(nameTarget,q); strcat(nameTarget,pathTarget); if (nameTarget[strlen(nameTarget)-1]=='\\') strcat(nameTarget,uselfn?lname:name); strcat(nameTarget,q); //Special variable to ensure that copy * a_file, where a_file is not a directory concats. bool special = second_file_of_current_source && target_is_file; second_file_of_current_source = true; if (special) oldsource.concat = true; //Don't create a new file when in concat mode if (oldsource.concat || DOS_CreateFile(nameTarget,0,&targetHandle)) { Bit32u dummy=0; //In concat mode. Open the target and seek to the eof if (!oldsource.concat || (DOS_OpenFile(nameTarget,OPEN_READWRITE,&targetHandle) && DOS_SeekFile(targetHandle,&dummy,DOS_SEEK_END))) { // Copy static Bit8u buffer[0x8000]; // static, otherwise stack overflow possible. bool failed = false; Bit16u toread = 0x8000; do { failed |= DOS_ReadFile(sourceHandle,buffer,&toread); failed |= DOS_WriteFile(targetHandle,buffer,&toread); } while (toread==0x8000); failed |= DOS_CloseFile(sourceHandle); failed |= DOS_CloseFile(targetHandle); if (strcmp(name,lname)&&uselfn) WriteOut(" %s [%s]\n",lname,name); else WriteOut(" %s\n",uselfn?lname:name); if(!source.concat && !special) count++; //Only count concat files once } else { DOS_CloseFile(sourceHandle); WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast<char*>(target.filename.c_str())); } } else { DOS_CloseFile(sourceHandle); WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast<char*>(target.filename.c_str())); } } else WriteOut(MSG_Get("SHELL_CMD_COPY_FAILURE"),const_cast<char*>(source.filename.c_str())); }; //On to the next file if the previous one wasn't a device if ((attr&DOS_ATTR_DEVICE) == 0) ret = DOS_FindNext(); else ret = false; }; } WriteOut(MSG_Get("SHELL_CMD_COPY_SUCCESS"),count); dos.dta(save_dta); }