/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    init.c

Abstract:

    This is the initialization module for the INSTALER program.

Author:

    Steve Wood (stevewo) 09-Aug-1994

Revision History:

--*/

#include "instaler.h"

#if TRACE_ENABLED
ULONG EnabledTraceEvents = DBG_MASK_INTERNALERROR |
                           DBG_MASK_MEMORYERROR;
#endif

MODULE_INFO ModuleInfo[ MAXIMUM_MODULE_INDEX ] = {
    {L"NTDLL", NULL},
    {L"KERNEL32", NULL},
    {L"ADVAPI32", NULL}
};



API_INFO ApiInfo[ MAXIMUM_API_INDEX ] = {
    {NTDLL_MODULE_INDEX,    "NtCreateFile",                 NULL, NtCreateFileHandler               , sizeof( CREATEFILE_PARAMETERS )                   , sizeof( NTSTATUS )},
    {NTDLL_MODULE_INDEX,    "NtOpenFile",                   NULL, NtOpenFileHandler                 , sizeof( OPENFILE_PARAMETERS )                     , sizeof( NTSTATUS )},
    {NTDLL_MODULE_INDEX,    "NtDeleteFile",                 NULL, NtDeleteFileHandler               , sizeof( DELETEFILE_PARAMETERS )                   , sizeof( NTSTATUS )},
    {NTDLL_MODULE_INDEX,    "NtSetInformationFile",         NULL, NtSetInformationFileHandler       , sizeof( SETINFORMATIONFILE_PARAMETERS )           , sizeof( NTSTATUS )},
    {NTDLL_MODULE_INDEX,    "NtQueryAttributesFile",        NULL, NtQueryAttributesFileHandler      , sizeof( QUERYATTRIBUTESFILE_PARAMETERS )          , sizeof( NTSTATUS )},
    {NTDLL_MODULE_INDEX,    "NtQueryDirectoryFile",         NULL, NtQueryDirectoryFileHandler       , sizeof( QUERYDIRECTORYFILE_PARAMETERS )           , sizeof( NTSTATUS )},
    {NTDLL_MODULE_INDEX,    "NtCreateKey",                  NULL, NtCreateKeyHandler                , sizeof( CREATEKEY_PARAMETERS )                    , sizeof( NTSTATUS )},
    {NTDLL_MODULE_INDEX,    "NtOpenKey",                    NULL, NtOpenKeyHandler                  , sizeof( OPENKEY_PARAMETERS )                      , sizeof( NTSTATUS )},
    {NTDLL_MODULE_INDEX,    "NtDeleteKey",                  NULL, NtDeleteKeyHandler                , sizeof( DELETEKEY_PARAMETERS )                    , sizeof( NTSTATUS )},
    {NTDLL_MODULE_INDEX,    "NtSetValueKey",                NULL, NtSetValueKeyHandler              , sizeof( SETVALUEKEY_PARAMETERS )                  , sizeof( NTSTATUS )},
    {NTDLL_MODULE_INDEX,    "NtDeleteValueKey",             NULL, NtDeleteValueKeyHandler           , sizeof( DELETEVALUEKEY_PARAMETERS )               , sizeof( NTSTATUS )},
    {NTDLL_MODULE_INDEX,    "NtClose",                      NULL, NtCloseHandleHandler              , sizeof( CLOSEHANDLE_PARAMETERS )                  , sizeof( NTSTATUS )},
    {KERNEL32_MODULE_INDEX, "GetVersion",                   NULL, GetVersionHandler                 , 0                                                 , sizeof( DWORD )},
    {KERNEL32_MODULE_INDEX, "GetVersionExW",                NULL, GetVersionExWHandler              , sizeof( GETVERSIONEXW_PARAMETERS )                , sizeof( BOOL )},
    {KERNEL32_MODULE_INDEX, "SetCurrentDirectoryA",         NULL, SetCurrentDirectoryAHandler       , sizeof( SETCURRENTDIRECTORYA_PARAMETERS )         , sizeof( BOOL )},
    {KERNEL32_MODULE_INDEX, "SetCurrentDirectoryW",         NULL, SetCurrentDirectoryWHandler       , sizeof( SETCURRENTDIRECTORYW_PARAMETERS )         , sizeof( BOOL )},
    {KERNEL32_MODULE_INDEX, "WritePrivateProfileStringA",   NULL, WritePrivateProfileStringAHandler , sizeof( WRITEPRIVATEPROFILESTRINGA_PARAMETERS )   , sizeof( DWORD )},
    {KERNEL32_MODULE_INDEX, "WritePrivateProfileStringW",   NULL, WritePrivateProfileStringWHandler , sizeof( WRITEPRIVATEPROFILESTRINGW_PARAMETERS )   , sizeof( DWORD )},
    {KERNEL32_MODULE_INDEX, "WritePrivateProfileSectionA",  NULL, WritePrivateProfileSectionAHandler, sizeof( WRITEPRIVATEPROFILESECTIONA_PARAMETERS )  , sizeof( DWORD )},
    {KERNEL32_MODULE_INDEX, "WritePrivateProfileSectionW",  NULL, WritePrivateProfileSectionWHandler, sizeof( WRITEPRIVATEPROFILESECTIONW_PARAMETERS )  , sizeof( DWORD )},
    {KERNEL32_MODULE_INDEX, "GetPrivateProfileStringW",     NULL, NULL, 0, sizeof( DWORD )},
    {KERNEL32_MODULE_INDEX, "GetPrivateProfileStringA",     NULL, NULL, 0, sizeof( DWORD )},
    {KERNEL32_MODULE_INDEX, "GetPrivateProfileSectionW",    NULL, NULL, 0, sizeof( DWORD )},
    {KERNEL32_MODULE_INDEX, "GetPrivateProfileSectionA",    NULL, NULL, 0, sizeof( DWORD )},
    {ADVAPI32_MODULE_INDEX, "RegConnectRegistryW",          NULL, RegConnectRegistryWHandler        , sizeof( REGCONNECTREGISTRYW_PARAMETERS )          , sizeof( DWORD )}
};

BOOLEAN
LoadApplicationForDebug(
    PWSTR CommandLine
    );

BOOLEAN
LoadDriveLetterDefinitions( VOID );

WCHAR WindowsDirectoryBuffer[ MAX_PATH ];
UNICODE_STRING WindowsDirectory;

BOOLEAN
InitializeInstaler(
    int argc,
    char *argv[]
    )
{
    UINT ModuleIndex, ApiIndex;
    PWSTR CommandLine;
    UINT i;
    LPSTR s;
    UCHAR c;
    SYSTEM_INFO SystemInfo;
    BOOLEAN ProcessingDebugSwitch;

    InitCommonCode( "INSTALER",
                    "[-9] [-r] [-dAE] CommandLine...",
                    "-9 specifies that GetVersion should lie and tell application\n"
                    "   that it is running on Windows 95\n"
                    "-r specifies that attempts to do a wildcard scan of the root\n"
                    "   directory of a drive should fail.\n"
                    "-d specifies one or more debugging message options\n"
                    "   A - shows all errors\n"
                    "   E - shows all debug events\n"
                    "   C - shows all create API calls\n"
                  );

    AppHeap = GetProcessHeap();
    InstalerModuleHandle = GetModuleHandle( NULL );

    GetWindowsDirectoryW( WindowsDirectoryBuffer, MAX_PATH );
    RtlInitUnicodeString( &WindowsDirectory, WindowsDirectoryBuffer );

    GetSystemInfo( &SystemInfo );

    TemporaryBufferLengthGrowth = SystemInfo.dwPageSize;
    TemporaryBufferMaximumLength = ((128 * 1024) + SystemInfo.dwAllocationGranularity - 1) &
                                   ~SystemInfo.dwAllocationGranularity;
    TemporaryBuffer = VirtualAlloc( NULL,
                                    TemporaryBufferMaximumLength,
                                    MEM_RESERVE,
                                    PAGE_READWRITE
                                  );
    if (TemporaryBuffer == NULL) {
        return FALSE;
        }

    InitializeListHead( &ProcessListHead );
    InitializeListHead( &FileReferenceListHead );
    InitializeListHead( &KeyReferenceListHead );
    InitializeListHead( &IniReferenceListHead );
    InitializeCriticalSection( &BreakTable );

    //
    // Loop over the table of modules and make sure each is loaded in our
    // address space so we can access their export tables.
    //
    for (ModuleIndex=0; ModuleIndex<MAXIMUM_MODULE_INDEX; ModuleIndex++) {
        ModuleInfo[ ModuleIndex ].ModuleHandle =
            GetModuleHandle( ModuleInfo[ ModuleIndex ].ModuleName );

        if (ModuleInfo[ ModuleIndex ].ModuleHandle == NULL) {
            //
            // Bail if any are missing.
            //
            DeclareError( INSTALER_MISSING_MODULE,
                          GetLastError(),
                          ModuleInfo[ ModuleIndex ].ModuleName
                        );
            return FALSE;
            }
        }

    //
    // Now loop over the table of entry points that we care about and
    // get the address of each entry point from the

    for (ApiIndex=0; ApiIndex<MAXIMUM_API_INDEX; ApiIndex++) {
        ModuleIndex = ApiInfo[ ApiIndex ].ModuleIndex;

        ApiInfo[ ApiIndex ].EntryPointAddress =
            LookupEntryPointInIAT( ModuleInfo[ ModuleIndex ].ModuleHandle,
                                   ApiInfo[ ApiIndex ].EntryPointName
                                 );

        if (ApiInfo[ ApiIndex ].EntryPointAddress == NULL) {
            //
            // Bail if we are unable to get the address of any of them
            //
            DeclareError( INSTALER_MISSING_ENTRYPOINT,
                          GetLastError(),
                          ApiInfo[ ApiIndex ].EntryPointName,
                          ModuleInfo[ ModuleIndex ].ModuleName
                        );
            return FALSE;
            }
        }

    CommandLine = NULL;
    ProcessingDebugSwitch = FALSE;
    AskUserOnce = FALSE;
    while (--argc) {
        s = *++argv;
        if (*s == '-' || *s == '/') {
            while (*++s) {
                switch( tolower( *s ) ) {
                    case '9':
                        DefaultGetVersionToWin95 = TRUE;
                        break;

                    case 'r':
                        FailAllScansOfRootDirectories = TRUE;
                        break;
#if TRACE_ENABLED
                    case 'd':
                        ProcessingDebugSwitch = TRUE;
                        break;

                    case 'a':
                        if (ProcessingDebugSwitch) {
                            EnabledTraceEvents = 0xFFFFFFFF;
                            break;
                            }
                    case 'e':
                        if (ProcessingDebugSwitch) {
                            EnabledTraceEvents |= DBG_MASK_DBGEVENT;
                            break;
                            }
                    case 'c':
                        if (ProcessingDebugSwitch) {
                            EnabledTraceEvents |= DBG_MASK_CREATEEVENT;
                            break;
                            }
#endif
                    default:
                        if (!ProcessingDebugSwitch) {
                            CommonSwitchProcessing( &argc, &argv, *s );
                            }
                        else {
                            Usage( "Invalid debug switch (-%c)", (ULONG)*s );
                            }
                        break;
                    }
                }

                ProcessingDebugSwitch = FALSE;
            }
        else
        if (!CommonArgProcessing( &argc, &argv )) {
            CommandLine = GetCommandLine();
            while (*CommandLine && *CommandLine <= L' ') {
                CommandLine += 1;
                }

            while (*CommandLine && *CommandLine > L' ') {
                CommandLine += 1;
                }

            CommandLine = wcsstr( CommandLine, GetArgAsUnicode( s ) );
            break;
            }
        }

    if (ImlPath == NULL) {
        Usage( "Must specify an installation name as first argument", 0 );
        }

    if (CommandLine == NULL) {
        Usage( "Command line missing", 0 );
        }

    pImlNew = CreateIml( ImlPath, FALSE );
    if (pImlNew == NULL) {
        FatalError( "Unable to create %ws (%u)\n",
                    (ULONG)ImlPath,
                    GetLastError()
                  );
        }

    //
    // Ready to roll.  Cache the drive letter information and then load
    // the application with the DEBUG option so we can watch what it does
    //
    if (!LoadDriveLetterDefinitions() ||
        !LoadApplicationForDebug( CommandLine )
       ) {
        return FALSE;
        }

    return TRUE;
}

BOOLEAN
LoadApplicationForDebug(
    PWSTR CommandLine
    )
{
    STARTUPINFO StartupInfo;
    PROCESS_INFORMATION ProcessInformation;
    PWSTR ImageFilePath;
    PWSTR ImageFileName;
    PWSTR CurrentDirectory;
    PWSTR TemporaryNull;
    WCHAR TemporaryChar;
    ULONG Length;
    WCHAR PathBuffer[ MAX_PATH ];

    TemporaryNull = CommandLine;
    while(TemporaryChar = *TemporaryNull) {
        if (TemporaryChar == L' ' || TemporaryChar == L'\t') {
            *TemporaryNull = UNICODE_NULL;
            break;
            }

        TemporaryNull += 1;
        }

    ImageFilePath = AllocMem( MAX_PATH * sizeof( WCHAR ) );
    Length = SearchPath( NULL,
                         CommandLine,
                         L".exe",
                         MAX_PATH,
                         ImageFilePath,
                         &ImageFileName
                       );
    *TemporaryNull = TemporaryChar;
    if (!Length || Length >= MAX_PATH) {
        DeclareError( INSTALER_CANT_DEBUG_PROGRAM,
                      GetLastError(),
                      CommandLine
                    );
        return FALSE;
        }

    if (ImageFileName != NULL &&
        ImageFileName > ImageFilePath &&
        ImageFileName[ -1 ] == L'\\'
       ) {
        ImageFileName[ -1 ] = UNICODE_NULL;
        CurrentDirectory = wcscpy( PathBuffer, ImageFilePath );
        ImageFileName[ -1 ] = L'\\';
        SetCurrentDirectory( CurrentDirectory );
        }
    else {
        CurrentDirectory = NULL;
        }

    memset( &StartupInfo, 0, sizeof( StartupInfo ) );
    StartupInfo.cb = sizeof(StartupInfo);

    //
    // Create the process with DEBUG option, as a separate WOW so we only see our
    // application's damage.
    //
    if (!CreateProcess( ImageFilePath,
                        CommandLine,
                        NULL,
                        NULL,
                        FALSE,                          // No handles to inherit
                        DEBUG_PROCESS |
                          CREATE_SEPARATE_WOW_VDM,      // Ignored for 32 bit apps
                        NULL,
                        CurrentDirectory,
                        &StartupInfo,
                        &ProcessInformation
                      )
       ) {
        DeclareError( INSTALER_CANT_DEBUG_PROGRAM,
                      GetLastError(),
                      CommandLine
                    );
        return FALSE;
        }
    else {
        sprintf( (LPSTR)PathBuffer, "%ws%ws.log", InstalerDirectory, InstallationName );
        InstalerLogFile = fopen( (LPSTR)PathBuffer, "w" );

        //
        // Will pick up the process and thread handles with the
        // CREATE_PROCESS_DEBUG_EVENT and CREATE_PROCESS_THREAD_EVENT
        //
        return TRUE;
        }
}


PVOID
LookupEntryPointInIAT(
    HMODULE ModuleHandle,
    PCHAR EntryPointName
    )
{
    PIMAGE_EXPORT_DIRECTORY Exports;
    PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionTableEntries;
    DWORD NumberOfFunctionTableEntries;
    ULONG ExportSize, FunctionTableSize;
    PCHAR *EntryPointNames;
    PULONG EntryPointAddresses;
    PUSHORT EntryPointOrdinals;
    PVOID EntryPointAddress;
    ULONG i;

    Exports = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData( ModuleHandle,
                                                                     TRUE,
                                                                     IMAGE_DIRECTORY_ENTRY_EXPORT,
                                                                     &ExportSize
                                                                   );
    if (Exports == NULL) {
        return NULL;
        }

    FunctionTableEntries = (PIMAGE_RUNTIME_FUNCTION_ENTRY)
        RtlImageDirectoryEntryToData( ModuleHandle,
                                      TRUE,
                                      IMAGE_DIRECTORY_ENTRY_EXCEPTION,
                                      &FunctionTableSize
                                    );
    if (FunctionTableEntries != NULL) {
        NumberOfFunctionTableEntries = FunctionTableSize / sizeof( IMAGE_RUNTIME_FUNCTION_ENTRY );
        }
    else {
        NumberOfFunctionTableEntries = 0;
        }

    EntryPointNames = (PCHAR *)((PCHAR)ModuleHandle + (ULONG)Exports->AddressOfNames);
    EntryPointOrdinals = (PUSHORT)((PCHAR)ModuleHandle + (ULONG)Exports->AddressOfNameOrdinals);
    EntryPointAddresses = (PULONG)((PCHAR)ModuleHandle + (ULONG)Exports->AddressOfFunctions);
    for (i=0; i<Exports->NumberOfNames; i++) {
        EntryPointAddress = (PVOID)((PCHAR)ModuleHandle +
                                    EntryPointAddresses[ EntryPointOrdinals[ i ] ]
                                   );
        if ((ULONG)EntryPointAddress > (ULONG)Exports &&
             (ULONG)EntryPointAddress < ((ULONG)Exports + ExportSize)
            ) {
            //
            // Skip this... It's a forwarded reference
            }
        else
        if (!_stricmp( EntryPointName, (PCHAR)ModuleHandle + (ULONG)EntryPointNames[ i ] )) {
            return GetAddressForEntryPointBreakpoint( EntryPointAddress,
                                                      NumberOfFunctionTableEntries,
                                                      FunctionTableEntries
                                                    );
            }
        }

    DbgEvent( INTERNALERROR, ( "Unable to find entry point '%s' in module at %x\n", EntryPointName, ModuleHandle ) );
    return NULL;
}


PVOID
GetAddressForEntryPointBreakpoint(
    PVOID EntryPointAddress,
    DWORD NumberOfFunctionTableEntries OPTIONAL,
    PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionTableEntries OPTIONAL
    )
{
    PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry;
    PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionTable;
    LONG High;
    LONG Low;
    LONG Middle;

    //
    // If no function table, then okay to set breakpoint at exported
    // address.
    //
    if (NumberOfFunctionTableEntries == 0) {
        return EntryPointAddress;
        }

    //
    // Initialize search indicies.
    //
    Low = 0;
    High = NumberOfFunctionTableEntries - 1;
    FunctionTable = FunctionTableEntries;

    //
    // Perform binary search on the function table for a function table
    // entry that subsumes the specified PC.
    //
    while (High >= Low) {
        //
        // Compute next probe index and test entry. If the specified PC
        // is greater than of equal to the beginning address and less
        // than the ending address of the function table entry, then
        // return the address of the function table entry. Otherwise,
        // continue the search.
        //
        Middle = (Low + High) >> 1;
        FunctionEntry = &FunctionTable[Middle];
        if ((ULONG)EntryPointAddress < FunctionEntry->BeginAddress) {
            High = Middle - 1;
            }
        else
        if ((ULONG)EntryPointAddress >= FunctionEntry->EndAddress) {
            Low = Middle + 1;
            }
        else {
            if (((ULONG)EntryPointAddress >= FunctionEntry->BeginAddress) &&
                ((ULONG)EntryPointAddress < FunctionEntry->PrologEndAddress)) {
                return (PVOID)FunctionEntry->PrologEndAddress;
                }
            else {
                break;
                }
            }
        }

    //
    // A function table entry for the specified PC was not found.  Just use
    // the exported address and hope for the best.
    //
    return EntryPointAddress;
}


BOOLEAN
LoadDriveLetterDefinitions( VOID )
{
    PVOID Buffer;
    ULONG BufferSize;
    ULONG cchDeviceName;
    ULONG cch;
    ULONG cchTargetPath;
    PWSTR DeviceName;
    PWSTR TargetPath;
    ULONG DriveLetterIndex;
    ULONG cb;
    PDRIVE_LETTER_INFO p;

    RtlInitUnicodeString( &AltDriveLetterPrefix, L"\\DosDevices\\" );
    RtlInitUnicodeString( &DriveLetterPrefix, L"\\??\\" );
    RtlInitUnicodeString( &UNCPrefix, L"UNC\\" );

    BufferSize = 0x1000;
    Buffer = VirtualAlloc( NULL, BufferSize, MEM_COMMIT, PAGE_READWRITE );
    if (Buffer == NULL) {
        DbgEvent( INTERNALERROR, ( "VirtualAlloc( %0x8 ) failed (%u)\n",
                                   BufferSize,
                                   GetLastError()
                                 )
                );

        return FALSE;
        }

    cchTargetPath = BufferSize / sizeof( WCHAR );
    cchDeviceName = QueryDosDeviceW( NULL,
                                     Buffer,
                                     cchTargetPath
                                   );
    if (cchDeviceName == 0) {
        DbgEvent( INTERNALERROR, ( "QueryDosDeviceW( NULL ) failed (%u)\n",
                                   GetLastError()
                                 )
                );
        return FALSE;
        }

    cchTargetPath -= (cchDeviceName + 2);
    DeviceName = Buffer;
    TargetPath = DeviceName + 2 + cchDeviceName;
    while (*DeviceName) {
        if (wcslen( DeviceName ) == 2 &&
            DeviceName[ 1 ] == L':' &&
            _wcsupr( DeviceName ) &&
            DeviceName[ 0 ] >= L'@' &&
            DeviceName[ 0 ] <= L'_'
           ) {
            cch = QueryDosDeviceW( DeviceName, TargetPath, cchTargetPath );
            if (cch == 0) {
                DbgEvent( INTERNALERROR, ( "QueryDosDeviceW( %ws ) failed (%u)\n",
                                           DeviceName,
                                           GetLastError()
                                         )
                        );
                return FALSE;
                }

            DriveLetterIndex = DeviceName[ 0 ] - L'@';
            p = &DriveLetters[ DriveLetterIndex ];
            p->DriveLetter = (WCHAR)(L'@' + DriveLetterIndex);
            cb = (cch+1) * sizeof( WCHAR );
            p->NtLinkTarget.Buffer = AllocMem( cb );
            if (p->NtLinkTarget.Buffer == NULL) {
                return FALSE;
                }
            MoveMemory( p->NtLinkTarget.Buffer, TargetPath, cb );
            p->NtLinkTarget.Length = (USHORT)( cb - sizeof( WCHAR ));
            p->NtLinkTarget.MaximumLength = (USHORT)cb;
            p->DriveLetterValid = TRUE;
            }

        while (*DeviceName++)
            ;
        }

    return TRUE;
}


BOOLEAN
IsDriveLetterPath(
    PUNICODE_STRING Path
    )
{
    ULONG DriveLetterIndex;
    PDRIVE_LETTER_INFO p;
    PUNICODE_STRING Prefix;

    if (RtlPrefixUnicodeString( Prefix = &DriveLetterPrefix, Path, TRUE ) ||
        RtlPrefixUnicodeString( Prefix = &AltDriveLetterPrefix, Path, TRUE )
       ) {
        Path->Length -= Prefix->Length;
        RtlMoveMemory( Path->Buffer,
                       (PCHAR)(Path->Buffer) + Prefix->Length,
                       Path->Length + sizeof( UNICODE_NULL )
                     );
        if (RtlPrefixUnicodeString( &UNCPrefix, Path, TRUE )) {
            Path->Length -= UNCPrefix.Length;
            Path->Buffer[0] = L'\\';
            Path->Buffer[1] = L'\\';
            RtlMoveMemory( &Path->Buffer[2],
                           (PCHAR)(Path->Buffer) + UNCPrefix.Length,
                           Path->Length + sizeof( UNICODE_NULL )
                         );
            Path->Length += 2 * sizeof( WCHAR );
            }

        return TRUE;
        }

    for (DriveLetterIndex=0, p = &DriveLetters[ DriveLetterIndex ];
         DriveLetterIndex<32;
         DriveLetterIndex++, p++
        ) {
        if (p->DriveLetterValid &&
            RtlPrefixUnicodeString( &p->NtLinkTarget, Path, TRUE )
           ) {
            Path->Length -= p->NtLinkTarget.Length;
            RtlMoveMemory( &Path->Buffer[ 2 ],
                           (PCHAR)(Path->Buffer) + p->NtLinkTarget.Length,
                           Path->Length + sizeof( UNICODE_NULL )
                         );
            Path->Buffer[ 0 ] = p->DriveLetter;
            Path->Buffer[ 1 ] = L':';
            Path->Length += 2;
            return TRUE;
            }
        }

    return FALSE;
}



VOID
TrimTemporaryBuffer( VOID )
{
    PVOID CommitAddress;

    if (TemporaryBufferLength == 0) {
        return;
        }

    if (VirtualFree( (PCHAR)TemporaryBuffer,
                     TemporaryBufferLength,
                     MEM_DECOMMIT
                   )
       ) {
        TemporaryBufferLength = 0;
        }

    return;
}

BOOLEAN
GrowTemporaryBuffer(
    ULONG NeededSize
    )
{
    PVOID CommitAddress;

    if (NeededSize <= TemporaryBufferLength) {
        return TRUE;
        }

    if (TemporaryBufferLength == TemporaryBufferMaximumLength) {
        return FALSE;
        }

    CommitAddress = VirtualAlloc( (PCHAR)TemporaryBuffer + TemporaryBufferLength,
                                  NeededSize - TemporaryBufferLength,
                                  MEM_COMMIT,
                                  PAGE_READWRITE
                                );
    if (CommitAddress == NULL) {
        DbgEvent( INTERNALERROR, ( "VirtualAlloc( %0x8 ) failed (%u)\n",
                                   NeededSize - TemporaryBufferLength,
                                   GetLastError()
                                 )
                );

        return FALSE;
        }

    TemporaryBufferLength += TemporaryBufferLengthGrowth;
    return TRUE;
}

ULONG
FillTemporaryBuffer(
    PPROCESS_INFO Process,
    PVOID Address,
    BOOLEAN Unicode,
    BOOLEAN DoubleNullTermination
    )
{
    ULONG BytesRead;
    ULONG TotalBytesRead;
    PVOID Source;
    PVOID Destination;
    LPSTR s1;
    PWSTR s2;
    BOOLEAN MoreToRead;
    BOOLEAN SeenOneNull;

    TotalBytesRead = 0;
    Destination = (PCHAR)TemporaryBuffer;
    MoreToRead = TRUE;
    SeenOneNull = FALSE;
    while (MoreToRead) {
        TotalBytesRead += TemporaryBufferLengthGrowth;
        if (!GrowTemporaryBuffer( TotalBytesRead )) {
            return 0;
            }

        Source = Destination;
        if (!ReadProcessMemory( Process->Handle,
                                Address,
                                Destination,
                                TemporaryBufferLengthGrowth,
                                &BytesRead
                              )
           ) {
            if (BytesRead == 0) {
                MoreToRead = FALSE;
                }
            else {
                return 0;
                }
            }

        Destination = (PCHAR)Destination + TotalBytesRead;
        if (Unicode) {
            s2 = (PWSTR)Source;
            while (s2 < (PWSTR)Destination) {
                if (*s2 == UNICODE_NULL) {
                    if (SeenOneNull || !DoubleNullTermination) {
                        return (PCHAR)s2 - (PCHAR)TemporaryBuffer;
                        }
                    else {
                        SeenOneNull = TRUE;
                        }
                    }
                else {
                    SeenOneNull = FALSE;
                    }

                s2++;
                }
            }
        else {
            s1 = (LPSTR)Source;
            while (s1 < (LPSTR)Destination) {
                if (*s1 == '\0') {
                    if (SeenOneNull || !DoubleNullTermination) {
                        return (PCHAR)s1 - (PCHAR)TemporaryBuffer;
                        }
                    else {
                        SeenOneNull = TRUE;
                        }
                    }
                else {
                    SeenOneNull = FALSE;
                    }

                s1++;
                }
            }
        }

    return 0;
}