Sync with trunk r63383 .
[reactos.git] / dll / win32 / kernel32 / client / vdm.c
index c37a3cf..8b50319 100644 (file)
@@ -93,26 +93,23 @@ BaseCheckVDM(IN ULONG BinaryType,
     STARTUPINFOA AnsiStartupInfo;
     ULONG NumStrings = 5;
 
-    if (CurrentDirectory == NULL)
+    /* Parameters validation */
+    if (ApplicationName == NULL || CommandLine == NULL)
     {
-        /* Allocate memory for the current directory path */
-        Length = GetCurrentDirectoryW(0, NULL);
-        CurrentDir = (PWCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
-                                             HEAP_ZERO_MEMORY,
-                                             Length * sizeof(WCHAR));
-        if (CurrentDir == NULL)
-        {
-            Status = STATUS_NO_MEMORY;
-            goto Cleanup;
-        }
-
-        /* Get the current directory */
-        GetCurrentDirectoryW(Length, CurrentDir);
-        CurrentDirectory = CurrentDir;
+        return STATUS_INVALID_PARAMETER;
     }
 
+    /* Trim leading whitespace from ApplicationName */
+    while (*ApplicationName == L' ' || *ApplicationName == L'\t')
+        ++ApplicationName;
+
     /* Calculate the size of the short application name */
     Length = GetShortPathNameW(ApplicationName, NULL, 0);
+    if (Length == 0)
+    {
+        Status = STATUS_OBJECT_PATH_INVALID;
+        goto Cleanup;
+    }
 
     /* Allocate memory for the short application name */
     ShortAppName = (PWCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
@@ -125,7 +122,7 @@ BaseCheckVDM(IN ULONG BinaryType,
     }
 
     /* Get the short application name */
-    if (!GetShortPathNameW(ApplicationName, ShortAppName, Length))
+    if (GetShortPathNameW(ApplicationName, ShortAppName, Length) == 0)
     {
         /* Try to determine which error occurred */
         switch (GetLastError())
@@ -151,6 +148,80 @@ BaseCheckVDM(IN ULONG BinaryType,
         goto Cleanup;
     }
 
+    /* Trim leading whitespace from CommandLine */
+    while (*CommandLine == L' ' || *CommandLine == L'\t')
+        ++CommandLine;
+
+    /*
+     * CommandLine is usually formatted as: 'ApplicationName param0 ...'.
+     * So we want to strip the first token (ApplicationName) from it.
+     * Two cases are in fact possible:
+     * - either the first token is indeed ApplicationName, so we just skip it;
+     * - or the first token is not exactly ApplicationName, because it happened
+     *   that somebody else already preprocessed CommandLine. Therefore we
+     *   suppose that the first token corresponds to an application name and
+     *   we skip it. Care should be taken when quotes are present in this token.
+     */
+     if (*CommandLine)
+     {
+        /* The first part of CommandLine should be the ApplicationName... */
+        Length = wcslen(ApplicationName);
+        if (Length <= wcslen(CommandLine) &&
+            _wcsnicmp(ApplicationName, CommandLine, Length) == 0)
+        {
+            /* Skip it */
+            CommandLine += Length;
+        }
+        /*
+         * ... but it is not, however we still have a token. We suppose that
+         * it corresponds to some sort of application name, so we skip it too.
+         */
+        else
+        {
+            /* Get rid of the first token. We stop when we see whitespace. */
+            while (*CommandLine && !(*CommandLine == L' ' || *CommandLine == L'\t'))
+            {
+                if (*CommandLine == L'\"')
+                {
+                    /* We enter a quoted part, skip it */
+                    ++CommandLine;
+                    while (*CommandLine && *CommandLine++ != L'\"') ;
+                }
+                else
+                {
+                    /* Go to the next character */
+                    ++CommandLine;
+                }
+            }
+        }
+    }
+
+    /*
+     * Trim remaining whitespace from CommandLine that may be
+     * present between the application name and the parameters.
+     */
+    while (*CommandLine == L' ' || *CommandLine == L'\t')
+        ++CommandLine;
+
+    /* Get the current directory */
+    if (CurrentDirectory == NULL)
+    {
+        /* Allocate memory for the current directory path */
+        Length = GetCurrentDirectoryW(0, NULL);
+        CurrentDir = (PWCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
+                                             HEAP_ZERO_MEMORY,
+                                             Length * sizeof(WCHAR));
+        if (CurrentDir == NULL)
+        {
+            Status = STATUS_NO_MEMORY;
+            goto Cleanup;
+        }
+
+        /* Get the current directory */
+        GetCurrentDirectoryW(Length, CurrentDir);
+        CurrentDirectory = CurrentDir;
+    }
+
     /* Calculate the size of the short current directory path */
     Length = GetShortPathNameW(CurrentDirectory, NULL, 0);
 
@@ -208,13 +279,14 @@ BaseCheckVDM(IN ULONG BinaryType,
     if (StartupInfo->dwFlags & STARTF_USESTDHANDLES)
     {
         /* Set the standard handles */
-        CheckVdm->StdIn = StartupInfo->hStdInput;
+        CheckVdm->StdIn  = StartupInfo->hStdInput;
         CheckVdm->StdOut = StartupInfo->hStdOutput;
         CheckVdm->StdErr = StartupInfo->hStdError;
     }
 
     /* Allocate memory for the ANSI strings */
-    AnsiCmdLine = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, CheckVdm->CmdLen);
+    // We need to add the newline characters '\r\n' to the command line
+    AnsiCmdLine = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, CheckVdm->CmdLen + 2);
     AnsiAppName = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, CheckVdm->AppLen);
     AnsiCurDirectory = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, CheckVdm->CurDirectoryLen);
     if (StartupInfo->lpDesktop) AnsiDesktop = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
@@ -247,6 +319,11 @@ BaseCheckVDM(IN ULONG BinaryType,
                         CheckVdm->CmdLen,
                         NULL,
                         NULL);
+    /* Add the needed newline and NULL-terminate */
+    CheckVdm->CmdLen--; // Rewind back to the NULL character
+    AnsiCmdLine[CheckVdm->CmdLen++] = '\r';
+    AnsiCmdLine[CheckVdm->CmdLen++] = '\n';
+    AnsiCmdLine[CheckVdm->CmdLen++] = 0;
 
     /* Convert the short application name into an ANSI string */
     WideCharToMultiByte(CP_ACP,
@@ -417,13 +494,13 @@ Cleanup:
     /* Free the capture buffer */
     CsrFreeCaptureBuffer(CaptureBuffer);
 
-    /* Free the short paths */
-    if (ShortAppName) RtlFreeHeap(RtlGetProcessHeap(), 0, ShortAppName);
+    /* Free the current directory, if it was allocated here, and its short path */
     if (ShortCurrentDir) RtlFreeHeap(RtlGetProcessHeap(), 0, ShortCurrentDir);
-
-    /* Free the current directory, if it was allocated here */
     if (CurrentDir) RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentDir);
 
+    /* Free the short app name */
+    if (ShortAppName) RtlFreeHeap(RtlGetProcessHeap(), 0, ShortAppName);
+
     return Status;
 }
 
@@ -662,7 +739,8 @@ BaseCreateVDMEnvironment(IN PWCHAR lpEnvironment,
 {
     BOOL Result;
     ULONG RegionSize, EnvironmentSize = 0;
-    PWCHAR p, Environment, NewEnvironment = NULL;
+    PWCHAR SourcePtr, DestPtr, Environment, NewEnvironment = NULL;
+    WCHAR PathBuffer[MAX_PATH];
     NTSTATUS Status;
 
     /* Make sure we have both strings */
@@ -695,8 +773,8 @@ BaseCreateVDMEnvironment(IN PWCHAR lpEnvironment,
     }
 
     /* Count how much space the whole environment takes */
-    p = Environment;
-    while ((*p++ != UNICODE_NULL) && (*p != UNICODE_NULL)) EnvironmentSize++;
+    SourcePtr = Environment;
+    while ((*SourcePtr++ != UNICODE_NULL) && (*SourcePtr != UNICODE_NULL)) EnvironmentSize++;
     EnvironmentSize += sizeof(UNICODE_NULL);
 
     /* Allocate a new copy */
@@ -715,16 +793,73 @@ BaseCreateVDMEnvironment(IN PWCHAR lpEnvironment,
     }
 
     /* Begin parsing the new environment */
-    p = NewEnvironment;
+    SourcePtr = Environment;
+    DestPtr   = NewEnvironment;
+
+    while (*SourcePtr != UNICODE_NULL)
+    {
+        while (*SourcePtr != UNICODE_NULL)
+        {
+            if (*SourcePtr == L'=')
+            {
+                /* Store the '=' sign */
+                *DestPtr++ = *SourcePtr++;
+
+                /* Check if this is likely a full path */
+                if (isalphaW(SourcePtr[0])
+                    && (SourcePtr[1] == L':')
+                    && ((SourcePtr[2] == '\\') || (SourcePtr[2] == '/')))
+                {
+                    PWCHAR Delimiter = wcschr(SourcePtr, L';');
+                    ULONG NumChars;
+
+                    if (Delimiter != NULL)
+                    {
+                        wcsncpy(PathBuffer,
+                                SourcePtr,
+                                min(Delimiter - SourcePtr, MAX_PATH));
+
+                        /* Seek to the part after the delimiter */
+                        SourcePtr = Delimiter + 1;
+                    }
+                    else
+                    {
+                        wcsncpy(PathBuffer, SourcePtr, MAX_PATH);
 
-    /* FIXME: Code here */
-    DPRINT1("BaseCreateVDMEnvironment is half-plemented!\n");
+                        /* Seek to the end of the string */
+                        SourcePtr = wcschr(SourcePtr, UNICODE_NULL);
+                    }
+
+                    /* Convert the path into a short path */
+                    NumChars = GetShortPathNameW(PathBuffer,
+                                                 DestPtr,
+                                                 EnvironmentSize - (DestPtr - NewEnvironment));
+                    if (NumChars)
+                    {
+                        /*
+                         * If it failed, this block won't be executed, so it
+                         * will continue from the character after the '=' sign.
+                         */
+                        DestPtr += NumChars;
+
+                        /* Append the delimiter */
+                        if (Delimiter != NULL) *DestPtr++ = L';';
+                    }
+                }
+            }
+            else if (islowerW(*SourcePtr)) *DestPtr++ = toupperW(*SourcePtr++);
+            else *DestPtr++ = *SourcePtr++;
+        }
+
+        /* Copy the terminating NULL character */
+        *DestPtr++ = *SourcePtr++;
+    }
 
     /* Terminate it */
-    *p++ = UNICODE_NULL;
+    *DestPtr++ = UNICODE_NULL;
 
     /* Initialize the unicode string to hold it */
-    EnvironmentSize = (p - NewEnvironment) * sizeof(WCHAR);
+    EnvironmentSize = (DestPtr - NewEnvironment) * sizeof(WCHAR);
     RtlInitEmptyUnicodeString(UnicodeEnv, NewEnvironment, (USHORT)EnvironmentSize);
     UnicodeEnv->Length = (USHORT)EnvironmentSize;
 
@@ -1267,6 +1402,16 @@ GetNextVDMCommand(PVDM_COMMAND_INFO CommandData)
 
                 if (!NT_SUCCESS(Status))
                 {
+                    /* Store the correct lengths */
+                    CommandData->CmdLen = GetNextVdmCommand->CmdLen;
+                    CommandData->AppLen = GetNextVdmCommand->AppLen;
+                    CommandData->PifLen = GetNextVdmCommand->PifLen;
+                    CommandData->CurDirectoryLen = GetNextVdmCommand->CurDirectoryLen;
+                    CommandData->EnvLen = GetNextVdmCommand->EnvLen;
+                    CommandData->DesktopLen = GetNextVdmCommand->DesktopLen;
+                    CommandData->TitleLen = GetNextVdmCommand->TitleLen;
+                    CommandData->ReservedLen = GetNextVdmCommand->ReservedLen;
+
                     BaseSetLastNTError(Status);
                     goto Cleanup;
                 }