/*++

Copyright (c) 2000 Microsoft Corporation

Module Name:

    opk.c

Abstract:

    Common modules shared by OPK tools.  Note: Source Depot requires that we 
    publish the .h (E:\NT\admin\published\ntsetup) and .lib 
    (E:\NT\public\internal\admin\lib).

Author:

    Brian Ku (briank) 06/20/2000

Revision History:

    7/00 - Jason Cohen (jcohen)
        Added in the rest of the common APIs form Millennium (need for lfnbk).

--*/


//
// Include file(s)
//

#include <pch.h>
#include <tchar.h>
#include <shlwapi.h>


//
// External Function(s):
//

LPTSTR AllocateString(HINSTANCE hInstance, UINT uID)
{
    //  ISSUE-2002/02/26-acosma - This sets a restriction of 256 characters on the buffer.
    //
    TCHAR   szBuffer[256];
    LPTSTR  lpBuffer = NULL;

    // Load the string from the resource and then allocate
    // a buffer just big enough for it.  Strings can exceed 256 characters.
    //
    if ( ( LoadString(hInstance, uID, szBuffer, sizeof(szBuffer) / sizeof(TCHAR)) ) &&
         ( lpBuffer = (LPTSTR) MALLOC(sizeof(TCHAR) * (lstrlen(szBuffer) + 1)) ) )
    {
        lstrcpy(lpBuffer, szBuffer);
    }

    // Return the allocated buffer, or NULL if there was an error.
    //
    return lpBuffer;
}

LPTSTR AllocateExpand(LPTSTR lpszBuffer)
{
    LPTSTR      lpszExpanded = NULL;
    DWORD       cbExpanded;

    // First we need to get the size of the expanded buffer and
    // allocate it.
    //
    if ( ( cbExpanded = ExpandEnvironmentStrings(lpszBuffer, NULL, 0) ) &&
         ( lpszExpanded = (LPTSTR) MALLOC(cbExpanded * sizeof(TCHAR)) ) )
    {
        // Now expand out the buffer.
        //
        if ( ( 0 == ExpandEnvironmentStrings(lpszBuffer, lpszExpanded, cbExpanded) ) ||
             ( NULLCHR == *lpszExpanded ) )
        {
            FREE(lpszExpanded);
        }
    }

    // Return the allocated buffer, or NULL if there was an error
    // or nothing in the string.
    //
    return lpszExpanded;
}

LPTSTR AllocateStrRes(HINSTANCE hInstance, LPSTRRES lpsrTable, DWORD cbTable, LPTSTR lpString, LPTSTR * lplpReturn)
{
    LPSTRRES    lpsrSearch  = lpsrTable;
    LPTSTR      lpReturn    = NULL;
    BOOL        bFound;

    // Init this return value.
    //
    if ( lplpReturn )
        *lplpReturn = NULL;

    // Try to find the friendly name for this string in our table.
    //
    while ( ( bFound = ((DWORD) (lpsrSearch - lpsrTable) < cbTable) ) &&
            ( lstrcmpi(lpString, lpsrSearch->lpStr) != 0 ) )
    {
        lpsrSearch++;
    }

    // If it was found, allocate the friendly name from the resource.
    //
    if ( bFound )
    {
        lpReturn = AllocateString(hInstance, lpsrSearch->uId);
        if ( lplpReturn )
            *lplpReturn = lpsrSearch->lpStr;
    }

    return lpReturn;
}

int MsgBoxLst(HWND hwndParent, LPTSTR lpFormat, LPTSTR lpCaption, UINT uType, va_list lpArgs)
{
    INT     nReturn;
    DWORD   dwCount     = 0;
    LPTSTR  lpText      = NULL;

    // The format string is required.
    //
    if ( lpFormat )
    {
        do
        {
            // Allocate 1k of characters at a time.
            //
            dwCount += 1024;

            // Free the previous buffer, if there was one.
            //
            FREE(lpText);

            // Allocate a new buffer.
            //
            if ( lpText = MALLOC(dwCount * sizeof(TCHAR)) )
                nReturn = _vsntprintf(lpText, dwCount, lpFormat, lpArgs);
            else
                nReturn = 0;
        }
        while ( nReturn < 0 );

        // Make sure we have the format string.
        //
        if ( lpText )
        {
            // Display the message box.
            //
            nReturn = MessageBox(hwndParent, lpText, lpCaption, uType);
            FREE(lpText);
        }
    }
    else
        nReturn = 0;

    // Return the return value of the MessageBox() call.  If there was a memory
    // error, 0 will be returned.
    //
    return nReturn;
}

int MsgBoxStr(HWND hwndParent, LPTSTR lpFormat, LPTSTR lpCaption, UINT uType, ...)
{
    va_list lpArgs;

    // Initialize the lpArgs parameter with va_start().
    //
    va_start(lpArgs, uType);

    // Return the return value of the MessageBox() call.  If there was a memory
    // error, 0 will be returned.  This is all 
    //
    return MsgBoxLst(hwndParent, lpFormat, lpCaption, uType, lpArgs);
}

int MsgBox(HWND hwndParent, UINT uFormat, UINT uCaption, UINT uType, ...)
{
    va_list lpArgs;
    INT     nReturn;
    LPTSTR  lpFormat    = NULL,
            lpCaption   = NULL;

    // Initialize the lpArgs parameter with va_start().
    //
    va_start(lpArgs, uType);

    // Get the format and caption strings from the resource.
    //
    if ( uFormat )
        lpFormat = AllocateString(NULL, uFormat);
    if ( uCaption )
        lpCaption = AllocateString(NULL, uCaption);

    // Return the return value of the MessageBox() call.  If there was a memory
    // error, 0 will be returned.
    //
    nReturn = MsgBoxLst(hwndParent, lpFormat, lpCaption, uType, lpArgs);

    // Free the format and caption strings.
    //
    FREE(lpFormat);
    FREE(lpCaption);    

    // Return the value saved from the previous function call.
    //
    return nReturn;
}

void CenterDialog(HWND hwnd)
{
    CenterDialogEx(NULL, hwnd); 
}


void CenterDialogEx(HWND hParent, HWND hChild)
{
    RECT rcChild,
         rcParent;

    if ( GetWindowRect(hChild, &rcChild) )
    {
        // If parent is specified center with respect to parent.
        if ( hParent && (GetWindowRect(hParent, &rcParent)) )
            SetWindowPos(hChild, NULL, ((rcParent.right + rcParent.left - (rcChild.right - rcChild.left)) / 2), ((rcParent.bottom + rcParent.top - (rcChild.bottom - rcChild.top)) / 2), 0, 0, SWP_NOSIZE | SWP_NOACTIVATE);

        // Otherwise center with respect to screen.
        //
        else 
            SetWindowPos(hChild, NULL, ((GetSystemMetrics(SM_CXSCREEN) - (rcChild.right - rcChild.left)) / 2), ((GetSystemMetrics(SM_CYSCREEN) - (rcChild.bottom - rcChild.top)) / 2), 0, 0, SWP_NOSIZE | SWP_NOACTIVATE);
    }
}
        

        


INT_PTR CALLBACK SimpleDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_INITDIALOG:
            //CenterDialog(hwnd);
            return FALSE;

        case WM_COMMAND:
            EndDialog(hwnd, LOWORD(wParam));
            return FALSE;

        default:
            return FALSE;
    }

    return TRUE;
}

INT_PTR SimpleDialogBox(HINSTANCE hInstance, LPCTSTR lpTemplate, HWND hWndParent)
{
    return DialogBox(hInstance, lpTemplate, hWndParent, SimpleDialogProc);
}

/****************************************************************************\

HFONT                       // Returns a valid handle to a font if it is
                            // successfully created, or NULL if something
                            // failed.  The font handle should be deleteted
                            // with DeleteObject() when it is no longer
                            // needed.

GetFont(                    // This function creates a font based in the info
                            // passed in.

    HWND hwndCtrl,          // Handle to a control that is used for the
                            // default font characteristics.  This may be
                            // NULL if not default is control is available.

    LPTSTR lpFontName,      // Points to a string that contains the name of
                            // the font to create. This parameter may be NULL
                            // if a valid control handle is passed in.  In
                            // that case, the font of the control is used.

    DWORD dwFontSize,       // Point size to use for the font.  If it is zero,
                            // the default is used.

    BOOL bSymbol            // If this is TRUE, the font is set to
                            // SYMBOL_CHARSET.  Typically this is FALSE.

);

\****************************************************************************/

HFONT GetFont(HWND hwndCtrl, LPTSTR lpFontName, DWORD dwFontSize, LONG lFontWeight, BOOL bSymbol)
{
    HFONT           hFont;
    LOGFONT         lFont;
    BOOL            bGetFont;

    // If the font name is passed in, then try to use that
    // first before getting the font of the control.
    //
    if ( lpFontName && *lpFontName )
    {
        // Make sure the font name is not longer than
        // 32 characters (including the NULL terminator).
        //
        if ( lstrlen(lpFontName) >= sizeof(lFont.lfFaceName) )
            return NULL;

        // Setup the structure to use to get the
        // font we want.
        //
        ZeroMemory(&lFont, sizeof(LOGFONT));
        lFont.lfCharSet = DEFAULT_CHARSET;
        lstrcpy(lFont.lfFaceName, lpFontName);
    }
        
    // First try to get the font that we wanted.
    //
    if ( ( lpFontName == NULL ) ||
         ( *lpFontName == NULLCHR ) ||
         ( (hFont = CreateFontIndirect((LPLOGFONT) &lFont)) == NULL ) )
    {
        // Couldn't get the font we wanted, try the font of the control
        // if a valid window handle was passed in.
        //
        if ( ( hwndCtrl == NULL ) ||
             ( (hFont = (HFONT) (WORD) SendMessage(hwndCtrl, WM_GETFONT, 0, 0L)) == NULL ) )
        {
            // All atempts to get the font failed.  We must return NULL.
            //
            return NULL;
        }
    }

    // Return the font we have now if we don't need to
    // change the size or weight.
    //
    if ( (lFontWeight == 0) && (dwFontSize == 0) )
        return hFont;

    // We must have a valid HFONT now.  Fill in the structure
    // and setup the size and weight we wanted for it.
    //
    bGetFont = GetObject(hFont, sizeof(LOGFONT), (LPVOID) &lFont);
    DeleteObject(hFont);

    if ( bGetFont )
    {
        // Set the bold and point size of the font.
        //
        if ( lFontWeight )
            lFont.lfWeight = lFontWeight;
        if ( dwFontSize )
            lFont.lfHeight = -MulDiv(dwFontSize, GetDeviceCaps(GetDC(NULL), LOGPIXELSY), 72);
        if ( bSymbol )
            lFont.lfCharSet = SYMBOL_CHARSET;

        // Create the font.
        //
        hFont = CreateFontIndirect((LPLOGFONT) &lFont);
    }
    else
        hFont = NULL;

    return hFont;
}

void ShowEnableWindow(HWND hwnd, BOOL bShowEnable)
{
    EnableWindow(hwnd, bShowEnable);
    ShowWindow(hwnd, bShowEnable ? SW_SHOW : SW_HIDE);
}

/****************************************************************************\

BOOL                        // Returns TRUE if we are running a server OS.

IsServer(                   // This routine checks if we're running on a
                            // Server OS.

    VOID

);

\****************************************************************************/

BOOL IsServer(VOID) 
{
    OSVERSIONINFOEX verInfo;
    BOOL            fReturn = FALSE;

    verInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    if ( ( GetVersionEx((LPOSVERSIONINFO) &verInfo) ) &&
         ( verInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ) &&
         ( ( verInfo.wProductType == VER_NT_SERVER ) ||
           ( verInfo.wProductType == VER_NT_DOMAIN_CONTROLLER ) ) )
    {
        fReturn = TRUE;
    }

    return fReturn;
}


BOOL IsIA64() 
/*++
===============================================================================
Routine Description:

    This routine checks if we're running on a 64-bit machine.

Arguments:

    None - 

Return Value:

    TRUE - We are running on 64-bit machine.
    FALSE - Not a 64-bit machine.

===============================================================================
--*/
{
    BOOL        fReturn         = FALSE;
    ULONG_PTR   Wow64Info       = 0;
    DWORD       dwSt            = 0;
    SYSTEM_INFO siSystemInfo;

    ZeroMemory( &siSystemInfo, sizeof(SYSTEM_INFO) );
    GetSystemInfo(&siSystemInfo);

    if ( (siSystemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) ||
         (siSystemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) )
        fReturn = TRUE;
    

    if (!fReturn)
    {
        // Now make sure that GetSystemInfo isn't lying because we are in emulation mode.
    
        dwSt = NtQueryInformationProcess(GetCurrentProcess(), ProcessWow64Information, &Wow64Info, sizeof(Wow64Info), NULL);
        if (!NT_SUCCESS(dwSt)) 
        {
            // Handle to process is bad, or the code is compiled 32-bit and running on NT4 or earlier.
            // Do nothing

        } 
        else if (Wow64Info)
        {
            // The process is 32-bit and is running inside WOW64.
            // We are really on IA64 and running in 32-bit mode.
            fReturn = TRUE;

        }
    }
    return fReturn;

}







BOOL ValidDosName(LPCTSTR lpName)
{
    LPCTSTR lpSearch    = lpName;
    int     nDot        = 0,
            nSpot       = 0,
            nLen;

    // Do the easy checks.
    //
    if ( ( lpSearch == NULL ) ||
         ( *lpSearch == NULLCHR ) ||
         ( (nLen = lstrlen(lpSearch)) > 12 ) )
    {
        return FALSE;
    }

    // Search the string.
    //
    for (; *lpSearch; lpSearch++)
    {
        // Check for dots.
        //
        if ( *lpSearch == _T('.') )
        {
            // Keep track of the number of dots.
            //
            nDot++;
            nSpot = (int) (lpSearch - lpName);
        }
        else
        {
            // Check for valid characters.
            //
            if ( !( ( ( _T('0') <= *lpSearch ) && ( *lpSearch <= _T('9') ) ) ||
                    ( ( _T('a') <= *lpSearch ) && ( *lpSearch <= _T('z') ) ) ||
                    ( ( _T('A') <= *lpSearch ) && ( *lpSearch <= _T('Z') ) ) ||
                    ( *lpSearch == _T('-') ) ||
                    ( *lpSearch == _T('_') ) ||
                    ( *lpSearch == _T('~') ) ) )
            {
                // Invalid character.
                //
                return FALSE;
            }
        }
    }

    // Make sure the dot is in the right place.
    //
    if ( ( nDot > 1 ) ||
         ( ( nDot == 0 ) && ( nLen > 8 ) ) ||
         ( ( nDot == 1 ) && ( nSpot > 8 ) ) ||
         ( ( nDot == 1 ) && ( (nLen - nSpot) > 4 ) ) )
    {
        return FALSE;
    }

    return TRUE;
}

DWORD GetLineArgs(LPTSTR lpSrc, LPTSTR ** lplplpArgs, LPTSTR * lplpAllArgs)
{
    LPTSTR  lpCmdLine,
            lpArg,
            lpDst;
    DWORD   dwArgs = 0;
    BOOL    bQuote;

    // Fist make sure that we were passed in a valid pointer, we have a command
    // line to parse, and that we were able to allocate the memory to hold it.
    //
    if ( ( lplplpArgs != NULL ) &&
         ( lpSrc ) &&
         ( *lpSrc ) &&
         ( lpCmdLine = (LPTSTR) MALLOC((lstrlen(lpSrc) + 1) * sizeof(TCHAR)) ) )
    {
        // Fist parse the command line into NULL terminated sub strings.
        //
        lpDst = lpCmdLine;
        while ( *lpSrc )
        {
            // Eat the preceeding spaces.
            //
            while ( *lpSrc == _T(' ') )
                lpSrc = CharNext(lpSrc);

            // Make sure we still have an argument.
            //
            if ( *lpSrc == _T('\0') )
                break;

            // Return a pointer to all the command line args if they want one.
            //
            if ( ( dwArgs == 1 ) && lplpAllArgs )
                *lplpAllArgs = lpSrc;

            // Save the current arg pointer.
            //
            lpArg = lpDst;
            dwArgs++;

            // See if we are looking for the next quote or space.
            //
            if ( bQuote = (*lpSrc == _T('"')) )
                lpSrc = CharNext(lpSrc);

            // Copy the argument into our allocated buffer until we
            // hit the separating character (which will always be a space).
            //
            while ( *lpSrc && ( bQuote || ( *lpSrc != _T(' ') ) ) )
            {
                // We special case the quote.
                //
                if ( *lpSrc == _T('"') )
                {
                    // If the character before the quote is a backslash, then
                    // we don't count this as the separating quote.
                    //
                    LPTSTR lpPrev = CharPrev(lpCmdLine, lpDst);
                    if ( lpPrev && ( *lpPrev == _T('\\') ) )
                        *lpPrev = *lpSrc++;
                    else
                    {
                        // Since we have found the separating quote, set this to
                        // false so we look for the next space.
                        //
                        bQuote = FALSE;
                        lpSrc++;
                    }                        
                }
                else
                    *lpDst++ = *lpSrc++;
            }

            // NULL terminate this argument.
            //
            *lpDst++ = _T('\0');
        }

        // Now setup the pointers to each argument.  Make sure we have some arguments
        // to return and that we have the memory allocated for the array.
        //
        if ( *lpCmdLine && dwArgs && ( *lplplpArgs = (LPTSTR *) MALLOC(dwArgs * sizeof(LPTSTR)) ) )
        {
            DWORD dwCount = 0;

            // Copy a pointer to each NULL terminated sub string into our
            // array of arguments we are going to return.
            //
            do
            {
                *(*lplplpArgs + dwCount) = lpCmdLine;
                lpCmdLine += lstrlen(lpCmdLine) + 1;
            }
            while ( ++dwCount < dwArgs );
        }
        else
        {
            // Either there were no command line arguments, or the memory allocation
            // failed for the list of arguments to return.
            //
            dwArgs = 0;
            FREE(lpCmdLine);
        }
    }

    return dwArgs;
}

DWORD GetCommandLineArgs(LPTSTR ** lplplpArgs)
{
    return GetLineArgs(GetCommandLine(), lplplpArgs, NULL);
}

// 
// Generic singularly linked list pvItem must be allocated with MALLOC
//
BOOL FAddListItem(PGENERIC_LIST* ppList, PGENERIC_LIST** pppNewItem, PVOID pvItem)
{    
    if (pppNewItem && *pppNewItem == NULL)
        *pppNewItem = ppList;

    if (*pppNewItem) {
        if (**pppNewItem = (PGENERIC_LIST)MALLOC(sizeof(GENERIC_LIST))) {
            (**pppNewItem)->pNext = NULL;
            (**pppNewItem)->pvItem = pvItem;
            *pppNewItem = &((**pppNewItem)->pNext);
            return TRUE;
        }
    }
    return FALSE;
}

void FreeList(PGENERIC_LIST pList)
{
    while (pList) {
        PGENERIC_LIST pTemp = pList;
        pList = pList->pNext;

        FREE(pTemp->pvItem);
        FREE(pTemp);
    }
}

// Find factory.exe from the current process, should be in same directory
//
BOOL FGetFactoryPath(LPTSTR pszFactoryPath)
{
    // Attempt to locate FACTORY.EXE
    //
    // NTRAID#NTBUG9-549770-2002/02/26-acosma,georgeje - Possible buffer overflow.
    //
    if (pszFactoryPath && GetModuleFileName(NULL, pszFactoryPath, MAX_PATH)) {
        if (PathRemoveFileSpec(pszFactoryPath)) {
            PathAppend(pszFactoryPath, TEXT("FACTORY.EXE"));
            if (FileExists(pszFactoryPath))
                return TRUE;
        }
    }
    return FALSE;
}

// Find sysprep.exe from the current process, should be in same directory
//
BOOL FGetSysprepPath(LPTSTR pszSysprepPath)
{
    // Attempt to locate SYSPREP.EXE
    //
    // NTRAID#NTBUG9-549770-2002/02/26-acosma,georgeje - Possible buffer overflow.
    //
    if (pszSysprepPath && GetModuleFileName(NULL, pszSysprepPath, MAX_PATH)) {
        if (PathRemoveFileSpec(pszSysprepPath)) {
            PathAppend(pszSysprepPath, TEXT("SYSPREP.EXE"));
            if (FileExists(pszSysprepPath))
                return TRUE;
        }
    }
    return FALSE;
}


//------------------------------------------------------------------------------------------------------
//
// Function:    ConnectNetworkResource
//
// Purpose:     This function allows the user to connect to a network resource with
//              supplied credentials.
//
// Arguments:   lpszPath:       Network Resource that should be shared out
//              lpszUsername:   Username for credentials, can be in form of domain\username
//              lpszPassword:   Password to use for credentials
//              bState:         If set to TRUE we will add the connection, if FALSE we will
//                              attempt to delete the connection
//
// Returns:     BOOL            If the NetUse command was successful, TRUE is returned
//
//------------------------------------------------------------------------------------------------------
NET_API_STATUS ConnectNetworkResource(LPTSTR lpszPath, LPTSTR lpszUsername, LPTSTR lpszPassword, BOOL bState)
{
    BOOL            bRet   = FALSE;
    USE_INFO_2      ui2;
    NET_API_STATUS  nerr_NetUse;
    TCHAR           szDomain[MAX_PATH]  = NULLSTR,
                    szNetUse[MAX_PATH]  = NULLSTR;
    LPTSTR          lpUser,
                    lpSearch;

    // Zero out the user information structure
    //
    ZeroMemory(&ui2, sizeof(ui2));

    // Copy the path into our buffer so we can work with it
    //
    lstrcpyn(szNetUse, lpszPath, AS(szNetUse));
    StrRTrm(szNetUse, CHR_BACKSLASH);

    if ( szNetUse[0] && PathIsUNC(szNetUse) )
    {
        // Disconnect from existing share
        //
        nerr_NetUse = NetUseDel(NULL, szNetUse, USE_NOFORCE);

        // We need to Add a connection
        //
        if ( bState )
        {
            ui2.ui2_remote      = szNetUse;
            ui2.ui2_asg_type    = USE_DISKDEV;
            ui2.ui2_password    = lpszPassword;
    
            lstrcpyn(szDomain, lpszUsername, AS(szDomain));

            // Break up the Domain\Username for the NetUse function
            //
            if (lpUser = StrChr(szDomain, CHR_BACKSLASH) )
            {
                // Put a NULL character after the domain part of the user name
                // and advance the pointer to point to the actual user name.
                //
                *(lpUser++) = NULLCHR;
            }
            else
            {
                // Use the computer name in the path as the domain name.
                //
                if ( lpSearch = StrChr(szNetUse + 2, CHR_BACKSLASH) )
                    lstrcpyn(szDomain, szNetUse + 2, (int)((lpSearch - (szNetUse + 2)) + 1));
                else
                    lstrcpyn(szDomain, szNetUse + 2, AS(szDomain));

                lpUser = lpszUsername;
            }

            // Set the domain and user name pointers into our struct.
            //
            ui2.ui2_domainname  = szDomain;
            ui2.ui2_username    = lpUser;

            // Create a connect to the share
            //
            nerr_NetUse = NetUseAdd(NULL, 2, (LPBYTE) &ui2, NULL);
        }
    }
    else
        nerr_NetUse = NERR_UseNotFound;

    // Return failure/success
    //
    return nerr_NetUse;
}

BOOL GetUncShare(LPCTSTR lpszPath, LPTSTR lpszShare, DWORD cbShare)
{
    BOOL    bRet;
    LPCTSTR lpSrc = lpszPath;
    LPTSTR  lpDst = lpszShare;
    DWORD   dwBackslashes,
            dwCount;

    // Make sure the path is a UNC by calling the shell function.
    //
    bRet = PathIsUNC(lpszPath);

    // This will loop through the path string twice, each time coping all the
    // backslashes and then all the non-backslashes.  So if the string passed
    // in was "\\COMPUTER\SHARE\DIR\FILE.NAME", the first pass will copy
    // "\\COMPUTER" and the next pass would then copy "\SHARE" so the final
    // string would then be "\\COMPUTER\SHARE".  This loop also verifies there
    // are the correct number of backslashes and non-backslashes.  If there is
    // no return buffer, then we just are verifing the share.
    //
    for ( dwBackslashes = 2; dwBackslashes && bRet; dwBackslashes-- )
    {
        // First copy the backslashes.
        //
        dwCount = 0;
        while ( _T('\\') == *lpSrc )
        {
            if ( lpDst && cbShare )
            {
                *lpDst++ = *lpSrc;
                cbShare--;
            }
            lpSrc++;
            dwCount++;
        }

        // Make sure the number of backslashes is correct.
        // The fist pass there should be two and the next
        // pass should be just one.
        //
        if ( dwBackslashes != dwCount )
        {
            bRet = FALSE;
        }
        else
        {
            // Now copy the non-backslashes.
            //
            dwCount = 0;
            while ( ( *lpSrc ) &&
                    ( _T('\\') != *lpSrc ) )
            {
                if ( lpDst && cbShare )
                {
                    *lpDst++ = *lpSrc;
                    cbShare--;
                }
                lpSrc++;
                dwCount++;
            }

            // Make sure there was at least one non-backslash.
            // character.
            //
            // Also if we are on the first pass and the path
            // buffer is already empty, then we don't have the
            // share part so just error out.
            //
            if ( ( 0 == dwCount ) ||
                 ( ( 2 == dwBackslashes ) &&
                   ( NULLCHR == *lpSrc ) ) )
            {
                bRet = FALSE;
            }
        }
    }

    // Only fix up the return buffer if there is one.
    //
    if ( lpszShare )
    {
        // Make sure that we didn't fail and that we still
        // have room for the null terminator.
        //
        if ( bRet && cbShare )
        {
            // Don't forget to null terminate the return string.
            //
            *lpDst = NULLCHR;
        }
        else
        {
            // If we failed or ran out of buffer room, make sure
            // we don't return anything.
            //
            *lpszShare = NULLCHR;
        }
    }

    return bRet;
}

DWORD GetSkuType()
{
    DWORD           dwRet = 0;
    OSVERSIONINFOEX osvi;

    ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

    if ( GetVersionEx((LPOSVERSIONINFO) &osvi) )
    {
        if ( VER_NT_WORKSTATION == osvi.wProductType )
        {
            if ( GET_FLAG(osvi.wSuiteMask, VER_SUITE_PERSONAL) )
            {
                dwRet = VER_SUITE_PERSONAL;
            }
            else
            {
                dwRet = VER_NT_WORKSTATION;
            }
        }
        else
        {
            if ( GET_FLAG(osvi.wSuiteMask, VER_SUITE_DATACENTER) )
            {
                dwRet = VER_SUITE_DATACENTER;
            }
            else if ( GET_FLAG(osvi.wSuiteMask, VER_SUITE_ENTERPRISE) )
            {
                dwRet = VER_SUITE_ENTERPRISE;
            }
            else if ( GET_FLAG(osvi.wSuiteMask, VER_SUITE_BLADE) )
            {
                dwRet = VER_SUITE_BLADE;
            }
            else
            {
                dwRet = VER_NT_SERVER;
            }
        }
    }

    return dwRet;
}