[NTVDM]: Fix (again) command-line parsing in DosCreateProcess.
[reactos.git] / reactos / subsystems / mvdm / ntvdm / dos / dos32krnl / process.c
index 75b975a..a841ab5 100644 (file)
@@ -46,6 +46,55 @@ static inline VOID DosSetPspCommandLine(WORD Segment, LPCSTR CommandLine)
     RtlCopyMemory(PspBlock->CommandLine, CommandLine, DOS_CMDLINE_LENGTH);
 }
 
+static inline VOID DosSaveState(VOID)
+{
+    PDOS_REGISTER_STATE State;
+    WORD StackPointer = getSP();
+
+    /* Allocate stack space for the registers */
+    StackPointer -= sizeof(DOS_REGISTER_STATE);
+    State = SEG_OFF_TO_PTR(getSS(), StackPointer);
+    setSP(StackPointer);
+
+    /* Save */
+    State->EAX = getEAX();
+    State->ECX = getECX();
+    State->EDX = getEDX();
+    State->EBX = getEBX();
+    State->ESP = getESP();
+    State->EBP = getEBP();
+    State->ESI = getESI();
+    State->EDI = getEDI();
+    State->DS = getDS();
+    State->ES = getES();
+    State->FS = getFS();
+    State->GS = getGS();
+    State->Flags = getEFLAGS();
+}
+
+static inline VOID DosRestoreState(VOID)
+{
+    PDOS_REGISTER_STATE State;
+
+    /* Pop the state structure from the stack */
+    State = SEG_OFF_TO_PTR(getSS(), getSP());
+    setSP(getSP() + sizeof(DOS_REGISTER_STATE));
+
+    /* Restore */
+    setEAX(State->EAX);
+    setECX(State->ECX);
+    setEDX(State->EDX);
+    setEBX(State->EBX);
+    setEBP(State->EBP);
+    setESI(State->ESI);
+    setEDI(State->EDI);
+    setDS(State->DS);
+    setES(State->ES);
+    setFS(State->FS);
+    setGS(State->GS);
+    setEFLAGS(State->Flags);
+}
+
 static WORD DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL,
                                     LPCSTR ProgramName)
 {
@@ -194,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;
@@ -205,15 +253,29 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
     WORD LoadSegment;
     WORD MaxAllocSize;
     DWORD i, FileSize;
+    CHAR FullPath[MAX_PATH];
+    CHAR ShortFullPath[MAX_PATH];
 
     /* 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))
+    {
+        /* Try to shorten the full path */
+        if (GetShortPathNameA(FullPath, ShortFullPath, sizeof(ShortFullPath)))
+        {
+            /* Use the shortened full path from now on */
+            ExecutablePath = ShortFullPath;
+        }
+    }
 
     /* Open a handle to the executable */
     FileHandle = CreateFileA(ExecutablePath,
@@ -346,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)
         {
@@ -390,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);
@@ -432,6 +495,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);
@@ -479,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);
@@ -503,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);
@@ -554,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();
@@ -590,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];
@@ -628,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';
 
@@ -709,8 +802,7 @@ Command:
                                        AppName,
                                        Parameters,
                                        CmdLine,
-                                       Env,
-                                       ReturnAddress);
+                                       Env);
             if (Result == ERROR_SUCCESS)
             {
                 /* Increment the re-entry count */
@@ -750,6 +842,9 @@ VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident)
     PDOS_MCB CurrentMcb;
     LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
     PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
+#ifndef STANDALONE
+    VDM_COMMAND_INFO CommandInfo;
+#endif
 
     DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
            Psp,
@@ -783,7 +878,7 @@ VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident)
             if (KeepResident)
             {
                 /* Check if this is the PSP block and we should reduce its size */
-                if (McbSegment == Psp && KeepResident < CurrentMcb->Size)
+                if ((McbSegment + 1) == Psp && KeepResident < CurrentMcb->Size)
                 {
                     /* Reduce the size of the block */
                     DosResizeMemory(McbSegment + 1, KeepResident, NULL);
@@ -818,29 +913,25 @@ Done:
         {
             ResetEvent(VdmTaskEvent);
             CpuUnsimulate();
+            return;
         }
     }
 
 #ifndef STANDALONE
-    // FIXME: This is probably not the best way to do it
-    /* Check if this was a nested DOS task */
-    if (CurrentPsp != SYSTEM_PSP)
-    {
-        VDM_COMMAND_INFO CommandInfo;
 
-        /* Decrement the re-entry count */
-        CommandInfo.TaskId = SessionId;
-        CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
-        GetNextVDMCommand(&CommandInfo);
+    /* Decrement the re-entry count */
+    CommandInfo.TaskId = SessionId;
+    CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
+    GetNextVDMCommand(&CommandInfo);
 
-        /* Clear the structure */
-        RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
+    /* Clear the structure */
+    RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
+
+    /* Update the VDM state of the task */
+    CommandInfo.TaskId = SessionId;
+    CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
+    GetNextVDMCommand(&CommandInfo);
 
-        /* Update the VDM state of the task */
-        CommandInfo.TaskId = SessionId;
-        CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
-        GetNextVDMCommand(&CommandInfo);
-    }
 #endif
 
     /* Save the return code - Normal termination */
@@ -850,6 +941,13 @@ Done:
     setSS(HIWORD(SEGMENT_TO_PSP(CurrentPsp)->LastStack));
     setSP(LOWORD(SEGMENT_TO_PSP(CurrentPsp)->LastStack));
 
+    /* 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),
                LOWORD(PspBlock->TerminateAddress));