Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1149 lines
31 KiB

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
fileutil.c
Abstract:
Implements utility routines for files, file paths, etc.
Author:
Jim Schmidt (jimschm) 08-Mar-2000
Revision History:
<alias> <date> <comments>
--*/
#include "pch.h"
//
// Includes
//
// None
#define DBG_FILEUTIL "FileUtil"
//
// Strings
//
// None
//
// Constants
//
// None
//
// Macros
//
// None
//
// Types
//
// None
//
// Globals
//
// None
//
// Macro expansion list
//
// None
//
// Private function prototypes
//
// None
//
// Macro expansion definition
//
// None
//
// Code
//
BOOL
pDefaultFindFileA (
IN PCSTR FileName
)
{
return (GetFileAttributesA (FileName) != INVALID_ATTRIBUTES);
}
BOOL
pDefaultFindFileW (
IN PCWSTR FileName
)
{
return (GetFileAttributesW (FileName) != INVALID_ATTRIBUTES);
}
BOOL
pDefaultSearchPathA (
IN PCSTR FileName,
IN DWORD BufferLength,
OUT PSTR Buffer
)
{
PSTR dontCare;
return SearchPathA (NULL, FileName, NULL, BufferLength, Buffer, &dontCare);
}
BOOL
pDefaultSearchPathW (
IN PCWSTR FileName,
IN DWORD BufferLength,
OUT PWSTR Buffer
)
{
PWSTR dontCare;
return SearchPathW (NULL, FileName, NULL, BufferLength, Buffer, &dontCare);
}
PCMDLINEA
ParseCmdLineExA (
IN PCSTR CmdLine,
IN PCSTR Separators, OPTIONAL
IN PFINDFILEA FindFileCallback, OPTIONAL
IN PSEARCHPATHA SearchPathCallback, OPTIONAL
IN OUT PGROWBUFFER Buffer
)
{
PFINDFILEA findFileCallback = FindFileCallback;
PSEARCHPATHA searchPathCallback = SearchPathCallback;
GROWBUFFER SpacePtrs = INIT_GROWBUFFER;
PCSTR p;
PSTR q;
INT Count;
INT i;
INT j;
PSTR *Array;
PCSTR Start;
CHAR OldChar = 0;
GROWBUFFER StringBuf = INIT_GROWBUFFER;
PBYTE CopyBuf;
PCMDLINEA CmdLineTable;
PCMDLINEARGA CmdLineArg;
ULONG_PTR Base;
PSTR Path = NULL;
PSTR UnquotedPath = NULL;
PSTR FixedFileName = NULL;
PSTR FirstArgPath = NULL;
DWORD pathSize = 0;
PCSTR FullPath = NULL;
BOOL fileExists = FALSE;
PSTR CmdLineCopy;
BOOL Quoted;
UINT OriginalArgOffset = 0;
UINT CleanedUpArgOffset = 0;
BOOL GoodFileFound = FALSE;
PSTR EndOfFirstArg;
BOOL QuoteMode = FALSE;
PSTR End;
if (!Separators) {
Separators = " =,;";
}
if (!findFileCallback) {
findFileCallback = pDefaultFindFileA;
}
if (!searchPathCallback) {
searchPathCallback = pDefaultSearchPathA;
}
pathSize = SizeOfStringA (CmdLine) * 2;
if (pathSize < MAX_MBCHAR_PATH) {
pathSize = MAX_MBCHAR_PATH;
}
Path = AllocTextA (pathSize);
UnquotedPath = AllocTextA (pathSize);
FixedFileName = AllocTextA (pathSize);
FirstArgPath = AllocTextA (pathSize);
CmdLineCopy = DuplicateTextA (CmdLine);
if (!Path ||
!UnquotedPath ||
!FixedFileName ||
!FirstArgPath ||
!CmdLineCopy
) {
return NULL;
}
//
// Build an array of places to break the string
//
for (p = CmdLineCopy ; *p ; p = _mbsinc (p)) {
if (_mbsnextc (p) == '\"') {
QuoteMode = !QuoteMode;
} else if (!QuoteMode &&
_mbschr (Separators, _mbsnextc (p))
) {
//
// Remove excess spaces
//
q = (PSTR) p + 1;
while (_mbsnextc (q) == ' ') {
q++;
}
if (q > p + 1) {
MoveMemory ((PBYTE) p + sizeof (CHAR), q, SizeOfStringA (q));
}
GbAppendPvoid (&SpacePtrs, p);
}
}
//
// Prepare the CMDLINE struct
//
CmdLineTable = (PCMDLINEA) GbGrow (Buffer, sizeof (CMDLINEA));
MYASSERT (CmdLineTable);
//
// NOTE: We store string offsets, then at the end resolve them
// to pointers later.
//
CmdLineTable->CmdLine = (PCSTR) (ULONG_PTR) StringBuf.End;
GbMultiSzAppendA (&StringBuf, CmdLine);
CmdLineTable->ArgCount = 0;
//
// Now test every combination, emulating CreateProcess
//
Count = SpacePtrs.End / sizeof (PVOID);
Array = (PSTR *) SpacePtrs.Buf;
i = -1;
EndOfFirstArg = NULL;
while (i < Count) {
GoodFileFound = FALSE;
Quoted = FALSE;
if (i >= 0) {
Start = Array[i] + 1;
} else {
Start = CmdLineCopy;
}
//
// Check for a full path at Start
//
if (_mbsnextc (Start) != '/') {
for (j = i + 1 ; j <= Count && !GoodFileFound ; j++) {
if (j < Count) {
OldChar = *Array[j];
*Array[j] = 0;
}
FullPath = Start;
//
// Remove quotes; continue in the loop if it has no terminating quotes
//
Quoted = FALSE;
if (_mbsnextc (Start) == '\"') {
StringCopyByteCountA (UnquotedPath, Start + 1, pathSize);
q = _mbschr (UnquotedPath, '\"');
if (q) {
*q = 0;
FullPath = UnquotedPath;
Quoted = TRUE;
} else {
FullPath = NULL;
}
}
if (FullPath && *FullPath) {
//
// Look in file system for the path
//
fileExists = findFileCallback (FullPath);
if (!fileExists && EndOfFirstArg) {
//
// Try prefixing the path with the first arg's path.
//
StringCopyByteCountA (
EndOfFirstArg,
FullPath,
pathSize - (HALF_PTR) ((PBYTE) EndOfFirstArg - (PBYTE) FirstArgPath)
);
FullPath = FirstArgPath;
fileExists = findFileCallback (FullPath);
}
if (!fileExists && i < 0) {
//
// Try appending .exe, then testing again. This
// emulates what CreateProcess does.
//
StringCopyByteCountA (
FixedFileName,
FullPath,
pathSize - sizeof (".exe")
);
q = GetEndOfStringA (FixedFileName);
q = _mbsdec (FixedFileName, q);
MYASSERT (q);
if (_mbsnextc (q) != '.') {
q = _mbsinc (q);
}
StringCopyA (q, ".exe");
FullPath = FixedFileName;
fileExists = findFileCallback (FullPath);
}
if (fileExists) {
//
// Full file path found. Test its file status, then
// move on if there are no important operations on it.
//
OriginalArgOffset = StringBuf.End;
GbMultiSzAppendA (&StringBuf, Start);
if (!StringMatchA (Start, FullPath)) {
CleanedUpArgOffset = StringBuf.End;
GbMultiSzAppendA (&StringBuf, FullPath);
} else {
CleanedUpArgOffset = OriginalArgOffset;
}
i = j;
GoodFileFound = TRUE;
}
}
if (j < Count) {
*Array[j] = OldChar;
}
}
if (!GoodFileFound) {
//
// If a wack is in the path, then we could have a relative path, an arg, or
// a full path to a non-existent file.
//
if (_mbschr (Start, '\\')) {
#ifdef DEBUG
j = i + 1;
if (j < Count) {
OldChar = *Array[j];
*Array[j] = 0;
}
DEBUGMSGA ((
DBG_VERBOSE,
"%s is a non-existent path spec, a relative path, or an arg",
Start
));
if (j < Count) {
*Array[j] = OldChar;
}
#endif
} else {
//
// The string at Start did not contain a full path; try using
// searchPathCallback.
//
for (j = i + 1 ; j <= Count && !GoodFileFound ; j++) {
if (j < Count) {
OldChar = *Array[j];
*Array[j] = 0;
}
FullPath = Start;
//
// Remove quotes; continue in the loop if it has no terminating quotes
//
Quoted = FALSE;
if (_mbsnextc (Start) == '\"') {
StringCopyByteCountA (UnquotedPath, Start + 1, pathSize);
q = _mbschr (UnquotedPath, '\"');
if (q) {
*q = 0;
FullPath = UnquotedPath;
Quoted = TRUE;
} else {
FullPath = NULL;
}
}
if (FullPath && *FullPath) {
if (searchPathCallback (
FullPath,
pathSize / sizeof (Path[0]),
Path
)) {
FullPath = Path;
} else if (i < 0) {
//
// Try appending .exe and searching the path again
//
StringCopyByteCountA (
FixedFileName,
FullPath,
pathSize - sizeof (".exe")
);
q = GetEndOfStringA (FixedFileName);
q = _mbsdec (FixedFileName, q);
MYASSERT (q);
if (_mbsnextc (q) != '.') {
q = _mbsinc (q);
}
StringCopyA (q, ".exe");
if (searchPathCallback (
FixedFileName,
pathSize / sizeof (Path[0]),
Path
)) {
FullPath = Path;
} else {
FullPath = NULL;
}
} else {
FullPath = NULL;
}
}
if (FullPath && *FullPath) {
fileExists = findFileCallback (FullPath);
MYASSERT (fileExists);
OriginalArgOffset = StringBuf.End;
GbMultiSzAppendA (&StringBuf, Start);
if (!StringMatchA (Start, FullPath)) {
CleanedUpArgOffset = StringBuf.End;
GbMultiSzAppendA (&StringBuf, FullPath);
} else {
CleanedUpArgOffset = OriginalArgOffset;
}
i = j;
GoodFileFound = TRUE;
}
if (j < Count) {
*Array[j] = OldChar;
}
}
}
}
}
CmdLineTable->ArgCount += 1;
CmdLineArg = (PCMDLINEARGA) GbGrow (Buffer, sizeof (CMDLINEARGA));
MYASSERT (CmdLineArg);
if (GoodFileFound) {
//
// We have a good full file spec in FullPath, its existance
// is in fileExists, and i has been moved to the space beyond
// the path. We now add a table entry.
//
CmdLineArg->OriginalArg = (PCSTR) (ULONG_PTR) OriginalArgOffset;
CmdLineArg->CleanedUpArg = (PCSTR) (ULONG_PTR) CleanedUpArgOffset;
CmdLineArg->Quoted = Quoted;
if (!EndOfFirstArg) {
StringCopyByteCountA (
FirstArgPath,
(PCSTR) (StringBuf.Buf + (ULONG_PTR) CmdLineArg->CleanedUpArg),
pathSize
);
q = (PSTR) GetFileNameFromPathA (FirstArgPath);
if (q) {
q = _mbsdec (FirstArgPath, q);
if (q) {
*q = 0;
}
}
EndOfFirstArg = AppendWackA (FirstArgPath);
}
} else {
//
// We do not have a good file spec; we must have a non-file
// argument. Put it in the table, and advance to the next
// arg.
//
j = i + 1;
if (j <= Count) {
if (j < Count) {
OldChar = *Array[j];
*Array[j] = 0;
}
CmdLineArg->OriginalArg = (PCSTR) (ULONG_PTR) StringBuf.End;
GbMultiSzAppendA (&StringBuf, Start);
Quoted = FALSE;
if (_mbschr (Start, '\"')) {
p = Start;
q = UnquotedPath;
End = (PSTR) ((PBYTE) UnquotedPath + pathSize - sizeof (CHAR));
while (*p && q < End) {
if (IsLeadByte (p)) {
*q++ = *p++;
*q++ = *p++;
} else {
if (*p == '\"') {
p++;
} else {
*q++ = *p++;
}
}
}
*q = 0;
CmdLineArg->CleanedUpArg = (PCSTR) (ULONG_PTR) StringBuf.End;
GbMultiSzAppendA (&StringBuf, UnquotedPath);
Quoted = TRUE;
} else {
CmdLineArg->CleanedUpArg = CmdLineArg->OriginalArg;
}
CmdLineArg->Quoted = Quoted;
if (j < Count) {
*Array[j] = OldChar;
}
i = j;
}
}
}
//
// We now have a command line table; transfer StringBuf to Buffer, then
// convert all offsets into pointers.
//
MYASSERT (StringBuf.End);
CopyBuf = GbGrow (Buffer, StringBuf.End);
MYASSERT (CopyBuf);
Base = (ULONG_PTR) CopyBuf;
CopyMemory (CopyBuf, StringBuf.Buf, StringBuf.End);
// Earlier GbGrow may have moved the buffer in memory. We need to repoint CmdLineTable
CmdLineTable = (PCMDLINEA)Buffer->Buf;
CmdLineTable->CmdLine = (PCSTR) ((PBYTE) CmdLineTable->CmdLine + Base);
CmdLineArg = &CmdLineTable->Args[0];
for (i = 0 ; i < (INT) CmdLineTable->ArgCount ; i++) {
CmdLineArg->OriginalArg = (PCSTR) ((PBYTE) CmdLineArg->OriginalArg + Base);
CmdLineArg->CleanedUpArg = (PCSTR) ((PBYTE) CmdLineArg->CleanedUpArg + Base);
CmdLineArg++;
}
GbFree (&StringBuf);
GbFree (&SpacePtrs);
FreeTextA (CmdLineCopy);
FreeTextA (FirstArgPath);
FreeTextA (FixedFileName);
FreeTextA (UnquotedPath);
FreeTextA (Path);
return (PCMDLINEA) Buffer->Buf;
}
PCMDLINEW
ParseCmdLineExW (
IN PCWSTR CmdLine,
IN PCWSTR Separators, OPTIONAL
IN PFINDFILEW FindFileCallback, OPTIONAL
IN PSEARCHPATHW SearchPathCallback, OPTIONAL
IN OUT PGROWBUFFER Buffer
)
{
PFINDFILEW findFileCallback = FindFileCallback;
PSEARCHPATHW searchPathCallback = SearchPathCallback;
GROWBUFFER SpacePtrs = INIT_GROWBUFFER;
PCWSTR p;
PWSTR q;
INT Count;
INT i;
INT j;
PWSTR *Array;
PCWSTR Start;
WCHAR OldChar = 0;
GROWBUFFER StringBuf = INIT_GROWBUFFER;
PBYTE CopyBuf;
PCMDLINEW CmdLineTable;
PCMDLINEARGW CmdLineArg;
ULONG_PTR Base;
PWSTR Path = NULL;
PWSTR UnquotedPath = NULL;
PWSTR FixedFileName = NULL;
PWSTR FirstArgPath = NULL;
DWORD pathSize = 0;
PCWSTR FullPath = NULL;
BOOL fileExists = FALSE;
PWSTR CmdLineCopy;
BOOL Quoted;
UINT OriginalArgOffset = 0;
UINT CleanedUpArgOffset = 0;
BOOL GoodFileFound = FALSE;
PWSTR EndOfFirstArg;
BOOL QuoteMode = FALSE;
PWSTR End;
if (!Separators) {
Separators = L" =,;";
}
if (!findFileCallback) {
findFileCallback = pDefaultFindFileW;
}
if (!searchPathCallback) {
searchPathCallback = pDefaultSearchPathW;
}
pathSize = SizeOfStringW (CmdLine);
if (pathSize < MAX_WCHAR_PATH) {
pathSize = MAX_WCHAR_PATH;
}
Path = AllocTextW (pathSize);
UnquotedPath = AllocTextW (pathSize);
FixedFileName = AllocTextW (pathSize);
FirstArgPath = AllocTextW (pathSize);
CmdLineCopy = DuplicateTextW (CmdLine);
if (!Path ||
!UnquotedPath ||
!FixedFileName ||
!FirstArgPath ||
!CmdLineCopy
) {
return NULL;
}
//
// Build an array of places to break the string
//
for (p = CmdLineCopy ; *p ; p++) {
if (*p == L'\"') {
QuoteMode = !QuoteMode;
} else if (!QuoteMode &&
wcschr (Separators, *p)
) {
//
// Remove excess spaces
//
q = (PWSTR) p + 1;
while (*q == L' ') {
q++;
}
if (q > p + 1) {
MoveMemory ((PBYTE) p + sizeof (WCHAR), q, SizeOfStringW (q));
}
GbAppendPvoid (&SpacePtrs, p);
}
}
//
// Prepare the CMDLINE struct
//
CmdLineTable = (PCMDLINEW) GbGrow (Buffer, sizeof (CMDLINEW));
MYASSERT (CmdLineTable);
//
// NOTE: We store string offsets, then at the end resolve them
// to pointers later.
//
CmdLineTable->CmdLine = (PCWSTR) (ULONG_PTR) StringBuf.End;
GbMultiSzAppendW (&StringBuf, CmdLine);
CmdLineTable->ArgCount = 0;
//
// Now test every combination, emulating CreateProcess
//
Count = SpacePtrs.End / sizeof (PVOID);
Array = (PWSTR *) SpacePtrs.Buf;
i = -1;
EndOfFirstArg = NULL;
while (i < Count) {
GoodFileFound = FALSE;
Quoted = FALSE;
if (i >= 0) {
Start = Array[i] + 1;
} else {
Start = CmdLineCopy;
}
//
// Check for a full path at Start
//
if (*Start != L'/') {
for (j = i + 1 ; j <= Count && !GoodFileFound ; j++) {
if (j < Count) {
OldChar = *Array[j];
*Array[j] = 0;
}
FullPath = Start;
//
// Remove quotes; continue in the loop if it has no terminating quotes
//
Quoted = FALSE;
if (*Start == L'\"') {
StringCopyByteCountW (UnquotedPath, Start + 1, pathSize);
q = wcschr (UnquotedPath, L'\"');
if (q) {
*q = 0;
FullPath = UnquotedPath;
Quoted = TRUE;
} else {
FullPath = NULL;
}
}
if (FullPath && *FullPath) {
//
// Look in file system for the path
//
fileExists = findFileCallback (FullPath);
if (!fileExists && EndOfFirstArg) {
//
// Try prefixing the path with the first arg's path.
//
StringCopyByteCountW (
EndOfFirstArg,
FullPath,
pathSize - (HALF_PTR) ((PBYTE) EndOfFirstArg - (PBYTE) FirstArgPath)
);
FullPath = FirstArgPath;
fileExists = findFileCallback (FullPath);
}
if (!fileExists && i < 0) {
//
// Try appending .exe, then testing again. This
// emulates what CreateProcess does.
//
StringCopyByteCountW (
FixedFileName,
FullPath,
pathSize - sizeof (L".exe")
);
q = GetEndOfStringW (FixedFileName);
q--;
MYASSERT (q >= FixedFileName);
if (*q != L'.') {
q++;
}
StringCopyW (q, L".exe");
FullPath = FixedFileName;
fileExists = findFileCallback (FullPath);
}
if (fileExists) {
//
// Full file path found. Test its file status, then
// move on if there are no important operations on it.
//
OriginalArgOffset = StringBuf.End;
GbMultiSzAppendW (&StringBuf, Start);
if (!StringMatchW (Start, FullPath)) {
CleanedUpArgOffset = StringBuf.End;
GbMultiSzAppendW (&StringBuf, FullPath);
} else {
CleanedUpArgOffset = OriginalArgOffset;
}
i = j;
GoodFileFound = TRUE;
}
}
if (j < Count) {
*Array[j] = OldChar;
}
}
if (!GoodFileFound) {
//
// If a wack is in the path, then we could have a relative path, an arg, or
// a full path to a non-existent file.
//
if (wcschr (Start, L'\\')) {
#ifdef DEBUG
j = i + 1;
if (j < Count) {
OldChar = *Array[j];
*Array[j] = 0;
}
DEBUGMSGW ((
DBG_VERBOSE,
"%s is a non-existent path spec, a relative path, or an arg",
Start
));
if (j < Count) {
*Array[j] = OldChar;
}
#endif
} else {
//
// The string at Start did not contain a full path; try using
// searchPathCallback.
//
for (j = i + 1 ; j <= Count && !GoodFileFound ; j++) {
if (j < Count) {
OldChar = *Array[j];
*Array[j] = 0;
}
FullPath = Start;
//
// Remove quotes; continue in the loop if it has no terminating quotes
//
Quoted = FALSE;
if (*Start == L'\"') {
StringCopyByteCountW (UnquotedPath, Start + 1, pathSize);
q = wcschr (UnquotedPath, L'\"');
if (q) {
*q = 0;
FullPath = UnquotedPath;
Quoted = TRUE;
} else {
FullPath = NULL;
}
}
if (FullPath && *FullPath) {
if (searchPathCallback (
FullPath,
pathSize / sizeof (Path[0]),
Path
)) {
FullPath = Path;
} else if (i < 0) {
//
// Try appending .exe and searching the path again
//
StringCopyByteCountW (
FixedFileName,
FullPath,
pathSize - sizeof (L".exe")
);
q = GetEndOfStringW (FixedFileName);
q--;
MYASSERT (q >= FixedFileName);
if (*q != L'.') {
q++;
}
StringCopyW (q, L".exe");
if (searchPathCallback (
FixedFileName,
pathSize / sizeof (Path[0]),
Path
)) {
FullPath = Path;
} else {
FullPath = NULL;
}
} else {
FullPath = NULL;
}
}
if (FullPath && *FullPath) {
fileExists = findFileCallback (FullPath);
MYASSERT (fileExists);
OriginalArgOffset = StringBuf.End;
GbMultiSzAppendW (&StringBuf, Start);
if (!StringMatchW (Start, FullPath)) {
CleanedUpArgOffset = StringBuf.End;
GbMultiSzAppendW (&StringBuf, FullPath);
} else {
CleanedUpArgOffset = OriginalArgOffset;
}
i = j;
GoodFileFound = TRUE;
}
if (j < Count) {
*Array[j] = OldChar;
}
}
}
}
}
CmdLineTable->ArgCount += 1;
CmdLineArg = (PCMDLINEARGW) GbGrow (Buffer, sizeof (CMDLINEARGW));
MYASSERT (CmdLineArg);
if (GoodFileFound) {
//
// We have a good full file spec in FullPath, its existance
// is in fileExists, and i has been moved to the space beyond
// the path. We now add a table entry.
//
CmdLineArg->OriginalArg = (PCWSTR) (ULONG_PTR) OriginalArgOffset;
CmdLineArg->CleanedUpArg = (PCWSTR) (ULONG_PTR) CleanedUpArgOffset;
CmdLineArg->Quoted = Quoted;
if (!EndOfFirstArg) {
StringCopyByteCountW (
FirstArgPath,
(PCWSTR) (StringBuf.Buf + (ULONG_PTR) CmdLineArg->CleanedUpArg),
pathSize
);
q = (PWSTR) GetFileNameFromPathW (FirstArgPath);
if (q) {
q--;
if (q >= FirstArgPath) {
*q = 0;
}
}
EndOfFirstArg = AppendWackW (FirstArgPath);
}
} else {
//
// We do not have a good file spec; we must have a non-file
// argument. Put it in the table, and advance to the next
// arg.
//
j = i + 1;
if (j <= Count) {
if (j < Count) {
OldChar = *Array[j];
*Array[j] = 0;
}
CmdLineArg->OriginalArg = (PCWSTR) (ULONG_PTR) StringBuf.End;
GbMultiSzAppendW (&StringBuf, Start);
Quoted = FALSE;
if (wcschr (Start, '\"')) {
p = Start;
q = UnquotedPath;
End = (PWSTR) ((PBYTE) UnquotedPath + pathSize - sizeof (WCHAR));
while (*p && q < End) {
if (*p == L'\"') {
p++;
} else {
*q++ = *p++;
}
}
*q = 0;
CmdLineArg->CleanedUpArg = (PCWSTR) (ULONG_PTR) StringBuf.End;
GbMultiSzAppendW (&StringBuf, UnquotedPath);
Quoted = TRUE;
} else {
CmdLineArg->CleanedUpArg = CmdLineArg->OriginalArg;
}
CmdLineArg->Quoted = Quoted;
if (j < Count) {
*Array[j] = OldChar;
}
i = j;
}
}
}
//
// We now have a command line table; transfer StringBuf to Buffer, then
// convert all offsets into pointers.
//
MYASSERT (StringBuf.End);
CopyBuf = GbGrow (Buffer, StringBuf.End);
MYASSERT (CopyBuf);
Base = (ULONG_PTR) CopyBuf;
CopyMemory (CopyBuf, StringBuf.Buf, StringBuf.End);
// Earlier GbGrow may have moved the buffer in memory. We need to repoint CmdLineTable
CmdLineTable = (PCMDLINEW)Buffer->Buf;
CmdLineTable->CmdLine = (PCWSTR) ((PBYTE) CmdLineTable->CmdLine + Base);
CmdLineArg = &CmdLineTable->Args[0];
for (i = 0 ; i < (INT) CmdLineTable->ArgCount ; i++) {
CmdLineArg->OriginalArg = (PCWSTR) ((PBYTE) CmdLineArg->OriginalArg + Base);
CmdLineArg->CleanedUpArg = (PCWSTR) ((PBYTE) CmdLineArg->CleanedUpArg + Base);
CmdLineArg++;
}
GbFree (&StringBuf);
GbFree (&SpacePtrs);
FreeTextW (CmdLineCopy);
FreeTextW (FirstArgPath);
FreeTextW (FixedFileName);
FreeTextW (UnquotedPath);
FreeTextW (Path);
return (PCMDLINEW) Buffer->Buf;
}