/*++

Copyright (c) 1997 Microsoft Corporation

Module Name:

    utils.c

Abstract:

    This source file implements utility functions used by scrnsave.c.

Author:

    Jim Schmidt (jimschm) 11-Apr-1997

Revision History:


--*/

#include "pch.h"

#ifdef UNICODE
#error UNICODE cannot be defined
#endif

//
// Declare strings
//

#define DEFMAC(var,str) CHAR var[] = str;
DEFINE_STRINGS
#undef DEFMAC

//
// Temporary buffer
//

static CHAR g_Data[MAX_PATH];

//
// Buffer for string representation of registry keys (for error logging)
//

typedef struct _tagKEYTOSTR {
    struct _tagKEYTOSTR *Prev, *Next;
    HKEY Key;
    CHAR RegKey[];
} KEYTOSTR, *PKEYTOSTR;

static PKEYTOSTR g_Head = NULL;

VOID
pAddKeyToStrMapping (
    IN      HKEY Key,
    IN      LPCSTR RootStr,
    IN      LPCSTR KeyStr
    )
{
    PKEYTOSTR Node;
    DWORD Size;
    CHAR FullKeyStr[MAX_PATH];

    // We control RootStr and KeyStr, so we know it is less than MAX_PATH in length
    wsprintf (FullKeyStr, "%s\\%s", RootStr, KeyStr);

    Size = sizeof (KEYTOSTR) + CountStringBytes (FullKeyStr);

    Node = (PKEYTOSTR) HeapAlloc (g_hHeap, 0, Size);
    if (Node) {
        Node->Prev = NULL;
        Node->Next = g_Head;
        Node->Key = Key;
        _mbscpy (Node->RegKey, FullKeyStr);

        if (g_Head) {
            g_Head->Prev = Node;
        }
        g_Head = Node;
    }
}

PKEYTOSTR
pFindKeyToStrMapping (
    IN      HKEY Key
    )
{
    PKEYTOSTR Node;

    Node = g_Head;
    while (Node) {
        if (Node->Key == Key) {
            return Node;
        }
        Node = Node->Next;
    }

    return NULL;
}

VOID
pRemoveKeyToStrMapping (
    IN      HKEY Key
    )
{
    PKEYTOSTR Node;

    Node = pFindKeyToStrMapping (Key);
    if (!Node) {
        return;
    }

    if (Node->Prev) {
        Node->Prev->Next = Node->Next;
    } else {
        g_Head = Node->Next;
    }

    if (Node->Next) {
        Node->Next->Prev = Node->Prev;
    }

    HeapFree (g_hHeap, 0, Node);
}
    

VOID
LogRegistryError (
    IN      HKEY Key,
    IN      LPCSTR ValueName
    )
{
    DWORD rc = GetLastError();
    LPCSTR FullKeyStr;
    PKEYTOSTR Node;

    Node = pFindKeyToStrMapping (Key);
    if (Node) {
        FullKeyStr = Node->RegKey;
    } else {
        FullKeyStr = S_DEFAULT_KEYSTR;
    }

    LOG ((LOG_ERROR, MSG_REGISTRY_ERROR, g_User, rc, FullKeyStr, ValueName));
}


VOID
GenerateFilePaths (
    VOID
    )
{
    INT Len;

    // Safety (unexpected condition)
    if (!g_WorkingDirectory) {
        return;
    }

    Len = CountStringBytes (g_WorkingDirectory) + sizeof(S_SETTINGS_MASK);
    g_SettingsFile = (LPSTR) HeapAlloc (g_hHeap, 0, Len);
    if (!g_SettingsFile) {
        return;
    }
    wsprintf (g_SettingsFile, S_SETTINGS_MASK, g_WorkingDirectory);

    Len = CountStringBytes (g_WorkingDirectory) + sizeof(S_MIGINF_MASK);
    g_MigrateDotInf = (LPSTR) HeapAlloc (g_hHeap, 0, Len);
    if (!g_MigrateDotInf) {
        return;
    }
    wsprintf (g_MigrateDotInf, S_MIGINF_MASK, g_WorkingDirectory);
}


HKEY
OpenRegKey (
    IN      HKEY RootKey,
    IN      LPCSTR KeyStr
    )
{
    HKEY Key;
    LONG rc;

    rc = RegOpenKeyEx (RootKey, KeyStr, 0, KEY_ALL_ACCESS, &Key);
    if (rc != ERROR_SUCCESS) {
        SetLastError (rc);
        return NULL;
    }

    pAddKeyToStrMapping (Key, S_HKR, KeyStr);

    return Key;
}


HKEY
CreateRegKey (
    IN      HKEY RootKey,
    IN      LPCSTR KeyStr
    )
{
    HKEY Key;
    LONG rc;
    DWORD DontCare;

    pAddKeyToStrMapping (NULL, S_HKR, KeyStr);

    rc = RegCreateKeyEx (RootKey, KeyStr, 0, S_EMPTY, 0, 
                         KEY_ALL_ACCESS, NULL, &Key, &DontCare);
    if (rc != ERROR_SUCCESS) {
        SetLastError (rc);
        LogRegistryError (NULL, S_EMPTY);
        pRemoveKeyToStrMapping (NULL);
        return NULL;
    }

    pRemoveKeyToStrMapping (NULL);
    pAddKeyToStrMapping (Key, S_HKR, KeyStr);

    return Key;
}

VOID
CloseRegKey (
    IN      HKEY Key
    )
{
    pRemoveKeyToStrMapping (Key);
    RegCloseKey (Key);
}


LPCSTR
GetRegValueString (
    IN      HKEY Key,
    IN      LPCSTR ValueName
    )
{
    static CHAR DataBuf[MAX_PATH];
    DWORD Size;
    LONG rc;
    DWORD Type;
    DWORD d;

    Size = MAX_PATH;
    rc = RegQueryValueEx (Key, ValueName, NULL, &Type, DataBuf, &Size);
    SetLastError (rc);

    if (rc != ERROR_SUCCESS) {
        return NULL;
    }

    if (Type == REG_DWORD) {
        d = *((PDWORD) DataBuf);
        wsprintf (DataBuf, "%u", d);
    }
    else if (Type != REG_SZ) {
        return NULL;
    }

    return DataBuf;
}

BOOL
SetRegValueString (
    HKEY Key,
    LPCSTR ValueName,
    LPCSTR ValueStr
    )
{
    LONG rc;
    LPCSTR p;

    p = _mbschr (ValueStr, 0);
    p++;

    rc = RegSetValueEx (Key, ValueName, 0, REG_SZ, ValueStr, p - ValueStr);
    SetLastError (rc);

    if (rc != ERROR_SUCCESS) {
        LogRegistryError (Key, ValueName);
    }

    return rc == ERROR_SUCCESS;
}


LPCSTR
GetScrnSaveExe (
    VOID
    )
{
    CHAR IniFileSetting[MAX_PATH];

    GetPrivateProfileString (
            S_BOOT, 
            S_SCRNSAVE_EXE, 
            S_EMPTY, 
            IniFileSetting, 
            MAX_PATH, 
            S_SYSTEM_INI
            );

    if (!IniFileSetting[0]) {
        return NULL;
    }

    if (!OurGetLongPathName (IniFileSetting, g_Data)) {
        // File does not exist
        return NULL;
    }

    return g_Data[0] ? g_Data : NULL;
}

INT
_mbsbytes (
    IN      LPCSTR str
    )
{
    LPCSTR p;

    // Find the nul terminator and return the number of bytes
    // occupied by the characters in the string, but don't
    // include the nul.

    p = _mbschr (str, 0);
    return (p - str);
}


DWORD
CountStringBytes (
    IN      LPCSTR str
    )
{
    // Return bytes in string, plus 1 for the nul
    return _mbsbytes (str) + 1;
}

DWORD
CountMultiStringBytes (
    IN      LPCSTR str
    )
{
    LPCSTR p;
    INT Total = 0;
    INT Bytes;

    p = str;

    do {
        Bytes = CountStringBytes (p);
        p += Bytes;
        Total += Bytes;
    } while (Bytes > 1);

    return Total;
}


LPSTR
CopyStringAtoB (
    OUT     LPSTR mbstrDest, 
    IN      LPCSTR mbstrStart, 
    IN      LPCSTR mbstrEnd     // first char NOT to copy
    )
{
    LPSTR mbstrOrg;

    mbstrOrg = mbstrDest;

    // Assume mbstrEnd is on a lead byte

    while (mbstrStart < mbstrEnd) {
        if (isleadbyte (*mbstrStart)) {
            *mbstrDest = *mbstrStart;
            mbstrDest++;
            mbstrStart++;
        }

        *mbstrDest = *mbstrStart;
        mbstrDest++;
        mbstrStart++;
    }

    *mbstrDest = 0;

    return mbstrOrg;
}

LPSTR 
AppendStr (
    OUT     LPSTR mbstrDest, 
    IN      LPCSTR mbstrSrc
    )

{
    // Advance mbstrDest to end of string
    mbstrDest = _mbschr (mbstrDest, 0);

    // Copy string
    while (*mbstrSrc) {
        *mbstrDest = *mbstrSrc++;
        if (isleadbyte (*mbstrDest)) {
            mbstrDest++;
            *mbstrDest = *mbstrSrc++;
        }
        mbstrDest++;
    }

    *mbstrDest = 0;

    return mbstrDest;
}


BOOL
pFindShortName (
    IN      LPCTSTR WhatToFind,
    OUT     LPTSTR Buffer
    )
{
    WIN32_FIND_DATA fd;
    HANDLE hFind;

    hFind = FindFirstFile (WhatToFind, &fd);
    if (hFind == INVALID_HANDLE_VALUE) {
        return FALSE;
    }

    FindClose (hFind);
    _mbscpy (Buffer, fd.cFileName);

    return TRUE;
}


BOOL
OurGetLongPathName (
    IN      LPCSTR ShortPath,
    OUT     LPSTR Buffer
    )
{
    CHAR FullPath[MAX_PATH];
    LPSTR FilePart;
    LPSTR BufferEnd;
    LPSTR p, p2;
    CHAR c;

    //
    // Convert ShortPath into complete path name
    //

    if (!_mbschr (ShortPath, TEXT('\\'))) {
        if (!SearchPath (NULL, ShortPath, NULL, MAX_PATH, FullPath, &FilePart)) {
            return FALSE;
        }
    } else {
        GetFullPathName (ShortPath, MAX_PATH, FullPath, &FilePart);
    }

    //
    // Convert short path to long path
    //

    p = FullPath;

    // Don't process non-local paths
    if (!(*p) || _mbsnextc (_mbsinc (p)) != TEXT(':')) {
        _mbscpy (Buffer, FullPath);
        return TRUE;
    }

    p = _mbsinc (p);
    p = _mbsinc (p);
    if (_mbsnextc (p) != TEXT('\\')) {
        _mbscpy (Buffer, FullPath);
        return TRUE;
    }
    
    // Copy drive letter to buffer
    p = _mbsinc (p);
    CopyStringAtoB (Buffer, FullPath, p);
    BufferEnd = _mbschr (Buffer, 0);

    // Convert each portion of the path
    do {
        // Locate end of this file or dir
        p2 = _mbschr (p, TEXT('\\'));
        if (!p2) {
            p = _mbschr (p, 0);
        } else {
            p = p2;
        }

        // Look up file
        c = *p;
        *p = 0;
        if (!pFindShortName (FullPath, BufferEnd)) {
            return FALSE;
        }
        *p = c;

        // Move on to next part of path
        BufferEnd = _mbschr (BufferEnd, 0);
        if (*p) {
            p = _mbsinc (p);
            BufferEnd = AppendStr (BufferEnd, TEXT("\\"));
        }
    } while (*p);

    return TRUE;
}


BOOL
CreateScreenSaverParamKey (
    IN      LPCSTR ScreenSaverName,
    IN      LPCSTR ValueName,
    OUT     LPSTR Buffer
    )
{
    //
    // Make sure we cannot create a string bigger than MAX_PATH
    //

    if (_mbslen (ScreenSaverName) + 4 + _mbslen (ValueName) > MAX_PATH) {
        return FALSE;
    }

    //
    // Format the string with length of screen saver name, screen saver name,
    // and value name.
    //

    wsprintf (Buffer, "%03u/%s/%s", _mbslen (ScreenSaverName), ScreenSaverName, ValueName);

    return TRUE;
}

BOOL
DecodeScreenSaverParamKey (
    IN      LPCSTR EncodedString,
    OUT     LPSTR ScreenSaverName,
    OUT     LPSTR ValueName
    )
{
    INT Len;

    //
    // Validate encoded string.  It is in the form of ###/screen saver name/value name.
    //

    Len = atoi (EncodedString);
    if (Len < 0 || Len >= MAX_PATH || (Len - 5) > (INT) _mbslen (EncodedString)) {
        return FALSE;
    }

    if (EncodedString[3] != '/' || EncodedString[4 + Len] != '/') {
        return FALSE;
    }

    if (_mbslen (EncodedString + 5 + Len) >= MAX_PATH) {
        return FALSE;
    }

    //
    // Extract screen saver name and value name
    //

    _mbsncpy (ScreenSaverName, EncodedString + 4, Len);
    ScreenSaverName[Len] = 0;

    _mbscpy (ValueName, EncodedString + 5 + Len);
    return TRUE;
}

LPSTR
_mbsistr (
    IN      LPCSTR mbstrStr, 
    IN      LPCSTR mbstrSubStr
    )
{
    LPCSTR mbstrStart, mbstrStrPos, mbstrSubStrPos;
    LPCSTR mbstrEnd;

    mbstrEnd = (LPSTR) ((LPBYTE) mbstrStr + _mbsbytes (mbstrStr) - _mbsbytes (mbstrSubStr));

    for (mbstrStart = mbstrStr ; mbstrStart <= mbstrEnd ; mbstrStart = _mbsinc (mbstrStart)) {
        mbstrStrPos = mbstrStart;
        mbstrSubStrPos = mbstrSubStr;

        while (*mbstrSubStrPos && 
               _mbctolower ((INT) _mbsnextc (mbstrSubStrPos)) == _mbctolower ((INT) _mbsnextc (mbstrStrPos))) 
        {
            mbstrStrPos = _mbsinc (mbstrStrPos);
            mbstrSubStrPos = _mbsinc (mbstrSubStrPos);
        }

        if (!(*mbstrSubStrPos))
            return (LPSTR) mbstrStart;
    }

    return NULL;    
}

VOID
DeletePartOfString (
    IN      LPSTR Buffer,
    IN      DWORD CharsToDelete
    )
{
    LPSTR p;
    DWORD d;

    p = Buffer;
    for (d = 0 ; *p && d < CharsToDelete ; d++) {
        p = _mbsinc (p);
    }

    if (!(*p)) {
        *Buffer = 0;
    } else {
        MoveMemory (Buffer, p, CountStringBytes(p));
    }
}

VOID
InsertStringInString (
    IN      LPSTR Buffer,
    IN      LPCSTR StringToInsert
    )
{
    DWORD BytesToMove;
    DWORD BytesOfInsertedString;

    BytesToMove = CountStringBytes (Buffer);
    BytesOfInsertedString = _mbsbytes(StringToInsert);
    MoveMemory (Buffer + BytesOfInsertedString, 
                Buffer,
                BytesToMove
                );
    _mbsncpy (Buffer, StringToInsert, _mbslen (StringToInsert));
}



PCSTR
ParseMessage (
    UINT MessageId,
    ...
    )
{
    va_list list;
    PSTR RetStr = NULL;
    UINT RetStrSize;

    va_start (list, MessageId);

    RetStrSize = FormatMessageA (
                    FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_HMODULE,
                    (PSTR) g_hInst,
                    MessageId,
                    0,
                    (PSTR) (&RetStr),
                    1,
                    &list
                    );

    if (!RetStrSize && RetStr) {
        *RetStr = 0;
    }

    va_end (list);

    return RetStr;
}

VOID
FreeMessage (
    PCSTR Message
    )
{
    if (Message) {
        LocalFree ((PSTR) Message);
    }
}