[NTVDM]: Fix (again) command-line parsing in DosCreateProcess.
[reactos.git] / reactos / subsystems / mvdm / ntvdm / dos / dos32krnl / process.c
index 46f644d..a841ab5 100644 (file)
@@ -54,6 +54,7 @@ static inline VOID DosSaveState(VOID)
     /* Allocate stack space for the registers */
     StackPointer -= sizeof(DOS_REGISTER_STATE);
     State = SEG_OFF_TO_PTR(getSS(), StackPointer);
+    setSP(StackPointer);
 
     /* Save */
     State->EAX = getEAX();
@@ -74,12 +75,10 @@ static inline VOID DosSaveState(VOID)
 static inline VOID DosRestoreState(VOID)
 {
     PDOS_REGISTER_STATE State;
-    WORD StackPointer = getSP();
 
-    /* SS:SP points to the stack on the last entry to INT 21h */
-    StackPointer -= (STACK_FLAGS + 1) * 2;      /* Interrupt parameters */
-    StackPointer -= sizeof(DOS_REGISTER_STATE); /* Pushed state structure */
-    State = SEG_OFF_TO_PTR(getSS(), StackPointer);
+    /* Pop the state structure from the stack */
+    State = SEG_OFF_TO_PTR(getSS(), getSP());
+    setSP(getSP() + sizeof(DOS_REGISTER_STATE));
 
     /* Restore */
     setEAX(State->EAX);
@@ -244,8 +243,7 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
                         IN LPCSTR ExecutablePath,
                         IN PDOS_EXEC_PARAM_BLOCK Parameters,
                         IN LPCSTR CommandLine OPTIONAL,
-                        IN LPCSTR Environment OPTIONAL,
-                        IN DWORD ReturnAddress OPTIONAL)
+                        IN LPCSTR Environment OPTIONAL)
 {
     DWORD Result = ERROR_SUCCESS;
     HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
@@ -261,11 +259,12 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
     /* 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",
+    DPRINT1("DosLoadExecutable(%d, '%s', 0x%08X, 0x%08X, 0x%08X)\n",
             LoadType,
             ExecutablePath,
             Parameters,
-            ReturnAddress);
+            CommandLine,
+            Environment);
 
     /* Try to get the full path to the executable */
     if (GetFullPathNameA(ExecutablePath, sizeof(FullPath), FullPath, NULL))
@@ -409,7 +408,8 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
         Header = (PIMAGE_DOS_HEADER)Address;
 
         /* Get the base size of the file, in paragraphs (rounded up) */
-        BaseSize = (((Header->e_cp - 1) * 512) + Header->e_cblp + 0x0F) >> 4;
+        BaseSize = ((((Header->e_cp - (Header->e_cblp != 0)) * 512)
+                   + Header->e_cblp + 0x0F) >> 4) - Header->e_cparhdr;
 
         if (LoadType != DOS_LOAD_OVERLAY)
         {
@@ -453,8 +453,8 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
             DosChangeMemoryOwner(Segment, Segment);
             DosChangeMemoryOwner(EnvBlock, Segment);
 
-            /* Set INT 22h to the return address */
-            ((PULONG)BaseAddress)[0x22] = ReturnAddress;
+            /* Set INT 22h to the current CS:IP */
+            ((PULONG)BaseAddress)[0x22] = MAKELONG(getIP(), getCS());
 
             /* Create the PSP */
             DosCreatePsp(Segment, (WORD)TotalSize);
@@ -496,7 +496,14 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
         if (LoadType == DOS_LOAD_AND_EXECUTE)
         {
             /* Save the program state */
-            if (CurrentPsp != SYSTEM_PSP) DosSaveState();
+            if (CurrentPsp != SYSTEM_PSP)
+            {
+                /* Push the task state */
+                DosSaveState();
+
+                /* Update the last stack in the PSP */
+                SEGMENT_TO_PSP(CurrentPsp)->LastStack = MAKELONG(getSP(), getSS());
+            }
 
             /* Set the initial segment registers */
             setDS(Segment);
@@ -545,8 +552,8 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
             DosChangeMemoryOwner(Segment, Segment);
             DosChangeMemoryOwner(EnvBlock, Segment);
 
-            /* Set INT 22h to the return address */
-            ((PULONG)BaseAddress)[0x22] = ReturnAddress;
+            /* Set INT 22h to the current CS:IP */
+            ((PULONG)BaseAddress)[0x22] = MAKELONG(getIP(), getCS());
 
             /* Create the PSP */
             DosCreatePsp(Segment, MaxAllocSize);
@@ -569,6 +576,16 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
 
         if (LoadType == DOS_LOAD_AND_EXECUTE)
         {
+            /* Save the program state */
+            if (CurrentPsp != SYSTEM_PSP)
+            {
+                /* Push the task state */
+                DosSaveState();
+
+                /* Update the last stack in the PSP */
+                SEGMENT_TO_PSP(CurrentPsp)->LastStack = MAKELONG(getSP(), getSS());
+            }
+
             /* Set the initial segment registers */
             setDS(Segment);
             setES(Segment);
@@ -620,28 +637,34 @@ DWORD DosStartProcess(IN LPCSTR ExecutablePath,
                       IN LPCSTR Environment OPTIONAL)
 {
     DWORD Result;
-    LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
+
+    SIZE_T CmdLen = strlen(CommandLine);
+    DPRINT1("Starting '%s' ('%.*s')...\n",
+            ExecutablePath,
+            /* Display the command line without the terminating 0d 0a (and skip the terminating NULL) */
+            CmdLen >= 2 ? (CommandLine[CmdLen - 2] == '\r' ? CmdLen - 2
+                                                           : CmdLen)
+                        : CmdLen,
+            CommandLine);
 
     Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
                                ExecutablePath,
                                NULL,
                                CommandLine,
-                               Environment,
-                               IntVecTable[0x20]);
+                               Environment);
 
     if (Result != ERROR_SUCCESS) goto Quit;
 
+#ifndef STANDALONE
+    /* Update console title if we run in a separate console */
+    if (SessionId != 0)
+        SetConsoleTitleA(ExecutablePath);
+#endif
+
     /* Attach to the console */
     ConsoleAttach();
     VidBiosAttachToConsole();
 
-    // 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.
-    // (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);
     CpuSimulate();
@@ -656,14 +679,13 @@ Quit:
 
 #ifndef STANDALONE
 WORD DosCreateProcess(LPCSTR ProgramName,
-                      PDOS_EXEC_PARAM_BLOCK Parameters,
-                      DWORD ReturnAddress)
+                      PDOS_EXEC_PARAM_BLOCK Parameters)
 {
     DWORD Result;
     DWORD BinaryType;
     LPVOID Environment = NULL;
     VDM_COMMAND_INFO CommandInfo;
-    CHAR CmdLine[MAX_PATH]; // DOS_CMDLINE_LENGTH + 1
+    CHAR CmdLine[MAX_PATH + DOS_CMDLINE_LENGTH + 1];
     CHAR AppName[MAX_PATH];
     CHAR PifFile[MAX_PATH];
     CHAR Desktop[MAX_PATH];
@@ -694,18 +716,23 @@ WORD DosCreateProcess(LPCSTR ProgramName,
     StartupInfo.cb = sizeof(StartupInfo);
 
     /*
-     * Convert the DOS command line to Win32-compatible format.
+     * Convert the DOS command line to Win32-compatible format, by concatenating
+     * the program name with the converted command line.
      * Format of the DOS command line: 1 byte for size; 127 bytes for contents.
      */
+    CmdLinePtr = CmdLine;
+    strncpy(CmdLinePtr, ProgramName, MAX_PATH); // Concatenate the program name
+    CmdLinePtr += strlen(CmdLinePtr);
+    *CmdLinePtr++ = ' ';                        // Add separating space
+
     CmdLineSize = min(*(PBYTE)FAR_POINTER(Parameters->CommandLine), DOS_CMDLINE_LENGTH);
-    RtlCopyMemory(CmdLine,
+    RtlCopyMemory(CmdLinePtr,
                   (LPSTR)FAR_POINTER(Parameters->CommandLine) + 1,
                   CmdLineSize);
     /* NULL-terminate it */
-    CmdLine[CmdLineSize] = '\0';
+    CmdLinePtr[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';
 
@@ -775,8 +802,7 @@ Command:
                                        AppName,
                                        Parameters,
                                        CmdLine,
-                                       Env,
-                                       ReturnAddress);
+                                       Env);
             if (Result == ERROR_SUCCESS)
             {
                 /* Increment the re-entry count */
@@ -915,8 +941,12 @@ Done:
     setSS(HIWORD(SEGMENT_TO_PSP(CurrentPsp)->LastStack));
     setSP(LOWORD(SEGMENT_TO_PSP(CurrentPsp)->LastStack));
 
-    /* Restore the program state */
-    DosRestoreState();
+    /* Are we returning to DOS code? */
+    if (HIWORD(PspBlock->TerminateAddress) == DOS_CODE_SEGMENT)
+    {
+        /* Pop the task state */
+        DosRestoreState();
+    }
 
     /* Return control to the parent process */
     CpuExecute(HIWORD(PspBlock->TerminateAddress),