[NTVDM]
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Sat, 2 May 2015 01:23:27 +0000 (01:23 +0000)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Sat, 2 May 2015 01:23:27 +0000 (01:23 +0000)
- Fix command-line handling.
- Disable a hack introduced in r65012 for testing purposes.

svn path=/trunk/; revision=67505

reactos/subsystems/mvdm/ntvdm/dos/dem.c
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.c
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/process.c

index a1adf9e..8fe242c 100644 (file)
@@ -116,29 +116,33 @@ static VOID WINAPI DosCmdInterpreterBop(LPWORD Stack)
     {
         case 0x08:  // Launch external command
         {
-#define CMDLINE_LENGTH  1024
-
             BOOL Result;
             DWORD dwExitCode;
 
             LPSTR Command = (LPSTR)SEG_OFF_TO_PTR(getDS(), getSI());
-            LPSTR CmdPtr  = Command;
-            CHAR CommandLine[CMDLINE_LENGTH] = "";
+            CHAR CmdLine[sizeof("cmd.exe /c ") + DOS_CMDLINE_LENGTH + 1] = "";
+            LPSTR CmdLinePtr;
+            ULONG CmdLineLen;
             STARTUPINFOA StartupInfo;
             PROCESS_INFORMATION ProcessInformation;
 
-            /* NULL-terminate the command line by removing the return carriage character */
-            while (*CmdPtr && *CmdPtr != '\r') CmdPtr++;
-            *CmdPtr = '\0';
-
-            DPRINT1("CMD Run Command '%s'\n", Command);
-
             /* Spawn a user-defined 32-bit command preprocessor */
 
-            /* Build the command line */
             // FIXME: Use COMSPEC env var!!
-            strcpy(CommandLine, "cmd.exe /c ");
-            strcat(CommandLine, Command);
+            CmdLinePtr = CmdLine;
+            strcpy(CmdLinePtr, "cmd.exe /c ");
+            CmdLinePtr += strlen(CmdLinePtr);
+
+            /* Build a Win32-compatible command-line */
+            CmdLineLen = min(strlen(Command), sizeof(CmdLine) - strlen(CmdLinePtr) - 1);
+            RtlCopyMemory(CmdLinePtr, Command, CmdLineLen);
+            CmdLinePtr[CmdLineLen] = '\0';
+
+            /* Remove any trailing return carriage character and NULL-terminate the command line */
+            while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++;
+            *CmdLinePtr = '\0';
+
+            DPRINT1("CMD Run Command '%s' ('%s')\n", Command, CmdLine);
 
             RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
             RtlZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
@@ -148,7 +152,7 @@ static VOID WINAPI DosCmdInterpreterBop(LPWORD Stack)
             VidBiosDetachFromConsole();
 
             Result = CreateProcessA(NULL,
-                                    CommandLine,
+                                    CmdLine,
                                     NULL,
                                     NULL,
                                     TRUE,
@@ -159,7 +163,7 @@ static VOID WINAPI DosCmdInterpreterBop(LPWORD Stack)
                                     &ProcessInformation);
             if (Result)
             {
-                DPRINT1("Command '%s' launched successfully\n", Command);
+                DPRINT1("Command '%s' ('%s') launched successfully\n", Command, CmdLine);
 
                 /* Wait for process termination */
                 WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
@@ -173,7 +177,7 @@ static VOID WINAPI DosCmdInterpreterBop(LPWORD Stack)
             }
             else
             {
-                DPRINT1("Failed when launched command '%s'\n");
+                DPRINT1("Failed when launched command '%s' ('%s')\n", Command, CmdLine);
                 dwExitCode = GetLastError();
             }
 
@@ -210,7 +214,7 @@ CommandThreadProc(LPVOID Parameter)
     PVOID Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
 
     UNREFERENCED_PARAMETER(Parameter);
-    ASSERT(Env != NULL);
+    ASSERT(Env);
 
     do
     {
@@ -253,7 +257,11 @@ Command:
         }
 
         /* Start the process from the command line */
-        DPRINT1("Starting '%s' ('%s')...\n", AppName, CmdLine);
+        DPRINT1("Starting '%s' ('%.*s')...\n",
+                AppName,
+                CommandInfo.CmdLen >= 2 ? CommandInfo.CmdLen - 2 /* Display the command line without the terminating 0d 0a */
+                                        : CommandInfo.CmdLen
+                CmdLine);
         Result = DosStartProcess(AppName, CmdLine, Env);
         if (Result != ERROR_SUCCESS)
         {
@@ -467,8 +475,7 @@ static VOID WINAPI DosStart(LPWORD Stack)
 
     /* Start the process from the command line */
     DPRINT1("Starting '%s' ('%s')...\n", ApplicationName, CommandLine);
-    Result = DosStartProcess(ApplicationName,
-                             CommandLine,
+    Result = DosStartProcess(ApplicationName, CommandLine,
                              SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0));
     if (Result != ERROR_SUCCESS)
     {
index 084f027..54d8b53 100644 (file)
@@ -161,59 +161,6 @@ static BOOLEAN DosControlBreak(VOID)
 
 /* PUBLIC FUNCTIONS ***********************************************************/
 
-VOID DosInitializePsp(WORD PspSegment,
-                      LPCSTR CommandLine,
-                      WORD ProgramSize,
-                      WORD Environment,
-                      DWORD ReturnAddress)
-{
-    PDOS_PSP PspBlock = SEGMENT_TO_PSP(PspSegment);
-    LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
-
-    RtlZeroMemory(PspBlock, sizeof(*PspBlock));
-
-    /* Set the exit interrupt */
-    PspBlock->Exit[0] = 0xCD; // int 0x20
-    PspBlock->Exit[1] = 0x20;
-
-    /* Set the number of the last paragraph */
-    PspBlock->LastParagraph = PspSegment + ProgramSize - 1;
-
-    /* Save the interrupt vectors */
-    PspBlock->TerminateAddress = ReturnAddress;
-    PspBlock->BreakAddress     = IntVecTable[0x23];
-    PspBlock->CriticalAddress  = IntVecTable[0x24];
-
-    /* Set the parent PSP */
-    PspBlock->ParentPsp = CurrentPsp;
-
-    /* Copy the parent handle table */
-    DosCopyHandleTable(PspBlock->HandleTable);
-
-    /* Set the environment block */
-    PspBlock->EnvBlock = Environment;
-
-    /* Set the handle table pointers to the internal handle table */
-    PspBlock->HandleTableSize = 20;
-    PspBlock->HandleTablePtr = MAKELONG(0x18, PspSegment);
-
-    /* Set the DOS version */
-    PspBlock->DosVersion = DOS_VERSION;
-
-    /* Set the far call opcodes */
-    PspBlock->FarCall[0] = 0xCD; // int 0x21
-    PspBlock->FarCall[1] = 0x21;
-    PspBlock->FarCall[2] = 0xCB; // retf
-
-    if (CommandLine)
-    {
-        /* Set the command line */
-        PspBlock->CommandLineSize = (BYTE)min(strlen(CommandLine), DOS_CMDLINE_LENGTH - 1);
-        RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
-        PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
-    }
-}
-
 VOID WINAPI DosInt20h(LPWORD Stack)
 {
     /* This is the exit interrupt */
@@ -1388,30 +1335,45 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Execute */
         case 0x4B:
         {
-            DOS_EXEC_TYPE LoadType = (DOS_EXEC_TYPE)getAL();
+            BYTE OrgAL = getAL();
             LPSTR ProgramName = SEG_OFF_TO_PTR(getDS(), getDX());
             PDOS_EXEC_PARAM_BLOCK ParamBlock = SEG_OFF_TO_PTR(getES(), getBX());
             DWORD ReturnAddress = MAKELONG(Stack[STACK_IP], Stack[STACK_CS]);
             WORD ErrorCode;
-            
+
+            if (OrgAL <= DOS_LOAD_OVERLAY)
+            {
+                DOS_EXEC_TYPE LoadType = (DOS_EXEC_TYPE)OrgAL;
+
 #ifndef STANDALONE
-            if (LoadType == DOS_LOAD_AND_EXECUTE)
+                if (LoadType == DOS_LOAD_AND_EXECUTE)
+                {
+                    /* Create a new process */
+                    ErrorCode = DosCreateProcess(ProgramName,
+                                                 ParamBlock,
+                                                 ReturnAddress);
+                }
+                else
+#endif
+                {
+                    /* Just load an executable */
+                    ErrorCode = DosLoadExecutable(LoadType,
+                                                  ProgramName,
+                                                  ParamBlock,
+                                                  NULL,
+                                                  NULL,
+                                                  ReturnAddress);
+                }
+            }
+            else if (OrgAL == 0x05)
             {
-                /* Create a new process */
-                ErrorCode = DosCreateProcess(ProgramName, ParamBlock, ReturnAddress);
+                // http://www.ctyme.com/intr/rb-2942.htm
+                DPRINT1("Set execution state is UNIMPLEMENTED\n");
+                ErrorCode = ERROR_CALL_NOT_IMPLEMENTED;
             }
             else
             {
-#else
-            {
-#endif
-                /* Just load an executable */
-                ErrorCode = DosLoadExecutable(LoadType,
-                                              ProgramName,
-                                              ParamBlock,
-                                              NULL,
-                                              NULL,
-                                              ReturnAddress);
+                ErrorCode = ERROR_INVALID_FUNCTION;
             }
 
             if (ErrorCode == ERROR_SUCCESS)
index 57024e0..d07183b 100644 (file)
@@ -37,10 +37,13 @@ static inline VOID DosSetPspCommandLine(WORD Segment, LPCSTR CommandLine)
 {
     PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
 
-    /* Set the command line */
-    PspBlock->CommandLineSize = (BYTE)min(strlen(CommandLine), DOS_CMDLINE_LENGTH - 1);
-    RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
-    PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
+    /*
+     * Copy the command line block.
+     * Format of the CommandLine parameter: 1 byte for size; 127 bytes for contents.
+     */
+    PspBlock->CommandLineSize = min(*(PBYTE)CommandLine, DOS_CMDLINE_LENGTH);
+    CommandLine++;
+    RtlCopyMemory(PspBlock->CommandLine, CommandLine, DOS_CMDLINE_LENGTH);
 }
 
 static WORD DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL,
@@ -115,8 +118,8 @@ static WORD DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL,
 
 VOID DosClonePsp(WORD DestSegment, WORD SourceSegment)
 {
-    PDOS_PSP DestPsp = SEGMENT_TO_PSP(DestSegment);
-    PDOS_PSP SourcePsp = SEGMENT_TO_PSP(SourceSegment);
+    PDOS_PSP DestPsp    = SEGMENT_TO_PSP(DestSegment);
+    PDOS_PSP SourcePsp  = SEGMENT_TO_PSP(SourceSegment);
     LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
 
     /* Literally copy the PSP first */
@@ -132,7 +135,7 @@ VOID DosClonePsp(WORD DestSegment, WORD SourceSegment)
 
     /* Set the handle table pointers to the internal handle table */
     DestPsp->HandleTableSize = DEFAULT_JFT_SIZE;
-    DestPsp->HandleTablePtr = MAKELONG(0x18, DestSegment);
+    DestPsp->HandleTablePtr  = MAKELONG(0x18, DestSegment);
 
     /* Copy the parent handle table without referencing the SFT */
     RtlCopyMemory(FAR_POINTER(DestPsp->HandleTablePtr),
@@ -142,7 +145,7 @@ VOID DosClonePsp(WORD DestSegment, WORD SourceSegment)
 
 VOID DosCreatePsp(WORD Segment, WORD ProgramSize)
 {
-    PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
+    PDOS_PSP PspBlock   = SEGMENT_TO_PSP(Segment);
     LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
 
     RtlZeroMemory(PspBlock, sizeof(*PspBlock));
@@ -170,7 +173,7 @@ VOID DosCreatePsp(WORD Segment, WORD ProgramSize)
 
     /* Set the handle table pointers to the internal handle table */
     PspBlock->HandleTableSize = DEFAULT_JFT_SIZE;
-    PspBlock->HandleTablePtr = MAKELONG(0x18, Segment);
+    PspBlock->HandleTablePtr  = MAKELONG(0x18, Segment);
 
     /* Set the DOS version */
     PspBlock->DosVersion = DOS_VERSION;
@@ -203,6 +206,9 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
     WORD MaxAllocSize;
     DWORD i, FileSize;
 
+    /* Buffer for command line conversion: 1 byte for size; 127 bytes for contents */
+    CHAR CmdLineBuffer[1 + DOS_CMDLINE_LENGTH];
+
     DPRINT1("DosLoadExecutable(%d, %s, 0x%08X, 0x%08X)\n",
             LoadType,
             ExecutablePath,
@@ -249,25 +255,73 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
 
     if (LoadType != DOS_LOAD_OVERLAY)
     {
-        LPSTR CmdLinePtr;
+        /* If an optional Win32 command line is given... */
+        if (CommandLine)
+        {
+            /* ... convert it into DOS format */
+            BYTE CmdLineLen;
+
+            PBYTE CmdLineSize  = (PBYTE)CmdLineBuffer;
+            LPSTR CmdLineStart = CmdLineBuffer + 1;
+            LPSTR CmdLinePtr   = CmdLineStart;
+
+            // For debugging purposes
+            RtlFillMemory(CmdLineBuffer, sizeof(CmdLineBuffer), 0xFF);
+
+            /*
+             * Set the command line: it is either an empty command line or has
+             * the format: " foo bar ..." (with at least one leading whitespace),
+             * and is then always followed by '\r' (and optionally by '\n').
+             */
+            CmdLineLen = (BYTE)strlen(CommandLine);
+            *CmdLineSize = 0;
+
+            /*
+             * Add the leading space if the command line is not empty
+             * and doesn't already start with some whitespace...
+             */
+            if (*CommandLine && *CommandLine != '\r' && *CommandLine != '\n' &&
+                *CommandLine != ' ' && *CommandLine != '\t')
+            {
+                (*CmdLineSize)++;
+                *CmdLinePtr++ = ' ';
+            }
 
-        if (CommandLine == NULL)
+            /* Compute the number of characters we need to copy from the original command line */
+            CmdLineLen = min(CmdLineLen, DOS_CMDLINE_LENGTH - *CmdLineSize);
+
+            /* The trailing '\r' or '\n' do not count in the PSP command line size parameter */
+            while (CmdLineLen && (CommandLine[CmdLineLen - 1] == '\r' || CommandLine[CmdLineLen - 1] == '\n'))
+            {
+                CmdLineLen--;
+            }
+
+            /* Finally, set everything up */
+            *CmdLineSize += CmdLineLen;
+            RtlCopyMemory(CmdLinePtr, CommandLine, CmdLineLen);
+            CmdLineStart[*CmdLineSize] = '\r';
+
+            /* Finally make the pointer point to the static buffer */
+            CommandLine = CmdLineBuffer;
+        }
+        else
         {
-            /* Get the command line from the parameter block */
+            /*
+             * ... otherwise, get the one from the parameter block.
+             * Format of the command line: 1 byte for size; 127 bytes for contents.
+             */
+            ASSERT(Parameters);
             CommandLine = (LPCSTR)FAR_POINTER(Parameters->CommandLine);
         }
 
+        /* If no optional environment is given... */
         if (Environment == NULL)
         {
-            /* Get the environment from the parameter block */
+            /* ... get the one from the parameter block */
+            ASSERT(Parameters);
             Environment = (LPCSTR)SEG_OFF_TO_PTR(Parameters->Environment, 0);
         }
 
-        /* NULL-terminate the command line by removing the return carriage character */
-        CmdLinePtr = (LPSTR)CommandLine;
-        while (*CmdLinePtr && *CmdLinePtr != '\r') CmdLinePtr++;
-        *CmdLinePtr = '\0';
-
         /* Copy the environment block to DOS memory */
         EnvBlock = DosCopyEnvironmentBlock(Environment, ExecutablePath);
         if (EnvBlock == 0)
@@ -352,6 +406,7 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
         }
         else
         {
+            ASSERT(Parameters);
             LoadSegment = Parameters->Overlay.Segment;
             RelocFactor = Parameters->Overlay.RelocationFactor;
         }
@@ -391,8 +446,9 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
         }
         else if (LoadType == DOS_LOAD_ONLY)
         {
+            ASSERT(Parameters);
             Parameters->StackLocation = MAKELONG(Header->e_sp, LoadSegment + Header->e_ss);
-            Parameters->EntryPoint = MAKELONG(Header->e_ip, LoadSegment + Header->e_cs);
+            Parameters->EntryPoint    = MAKELONG(Header->e_ip, LoadSegment + Header->e_cs);
         }
     }
     else
@@ -436,9 +492,11 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
         }
         else
         {
+            ASSERT(Parameters);
             LoadSegment = Parameters->Overlay.Segment;
         }
 
+        /* Copy the program to the code segment */
         RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0),
                       Address,
                       FileSize);
@@ -465,8 +523,9 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
         }
         else if (LoadType == DOS_LOAD_ONLY)
         {
+            ASSERT(Parameters);
             Parameters->StackLocation = MAKELONG(0xFFFE, Segment);
-            Parameters->EntryPoint = MAKELONG(0x0100, Segment);
+            Parameters->EntryPoint    = MAKELONG(0x0100, Segment);
         }
     }
 
@@ -513,8 +572,9 @@ DWORD DosStartProcess(IN LPCSTR ExecutablePath,
     // HACK: Simulate a ENTER key release scancode on the PS/2 port because
     // some apps expect to read a key release scancode (> 0x80) when they
     // are started.
-    IOWriteB(PS2_CONTROL_PORT, 0xD2);     // Next write is for the first PS/2 port
-    IOWriteB(PS2_DATA_PORT, 0x80 | 0x1C); // ENTER key release
+    // (hbelusca 2 May 2015: I'm not sure it's really useful. See r65012)
+    // IOWriteB(PS2_CONTROL_PORT, 0xD2);     // Next write is for the first PS/2 port
+    // IOWriteB(PS2_DATA_PORT, 0x80 | 0x1C); // ENTER key release
 
     /* Start simulation */
     SetEvent(VdmTaskEvent);
@@ -537,19 +597,25 @@ WORD DosCreateProcess(LPCSTR ProgramName,
     DWORD BinaryType;
     LPVOID Environment = NULL;
     VDM_COMMAND_INFO CommandInfo;
-    CHAR CmdLine[MAX_PATH];
+    CHAR CmdLine[MAX_PATH]; // DOS_CMDLINE_LENGTH + 1
     CHAR AppName[MAX_PATH];
     CHAR PifFile[MAX_PATH];
     CHAR Desktop[MAX_PATH];
     CHAR Title[MAX_PATH];
+    LPSTR CmdLinePtr;
+    ULONG CmdLineSize;
     ULONG EnvSize = 256;
-    PVOID Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
+    PVOID Env;
     STARTUPINFOA StartupInfo;
     PROCESS_INFORMATION ProcessInfo;
 
     /* Get the binary type */
     if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError();
 
+    /* Initialize Win32-VDM environment */
+    Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
+    if (Env == NULL) return GetLastError();
+
     /* Did the caller specify an environment segment? */
     if (Parameters->Environment)
     {
@@ -561,9 +627,25 @@ WORD DosCreateProcess(LPCSTR ProgramName,
     RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
     StartupInfo.cb = sizeof(StartupInfo);
 
+    /*
+     * Convert the DOS command line to Win32-compatible format.
+     * Format of the DOS command line: 1 byte for size; 127 bytes for contents.
+     */
+    CmdLineSize = min(*(PBYTE)FAR_POINTER(Parameters->CommandLine), DOS_CMDLINE_LENGTH);
+    RtlCopyMemory(CmdLine,
+                  FAR_POINTER(Parameters->CommandLine) + 1,
+                  CmdLineSize);
+    /* NULL-terminate it */
+    CmdLine[CmdLineSize] = '\0';
+
+    /* Remove any trailing return carriage character and NULL-terminate the command line */
+    CmdLinePtr = CmdLine;
+    while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++;
+    *CmdLinePtr = '\0';
+
     /* Create the process */
     if (!CreateProcessA(ProgramName,
-                        FAR_POINTER(Parameters->CommandLine),
+                        CmdLine,
                         NULL,
                         NULL,
                         FALSE,
@@ -573,6 +655,7 @@ WORD DosCreateProcess(LPCSTR ProgramName,
                         &StartupInfo,
                         &ProcessInfo))
     {
+        RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
         return GetLastError();
     }