/*****************************************************************************\
* MODULE: genutil.c
*
* Various common routines used throughout the gen* files.
*
*   routines
*   --------
*   genGetCurDir
*   genGetWinDir
*   genBuildFileName
*   genFindChar
*   genFindCharDiff
*   genFindRChar
*   genWCFromMB
*   genMBFromWC
*   genItoA
*
*   genIsWin9X
*   genIdxCliPlatform
*   genStrCliCab
*   genStrCliEnvironment
*   genStrCliOverride
*   genValCliArchitecture
*   genIdxCliVersion
*   genStrCliVersion
*   genValSvrArchitecture
*
*
* Copyright (C) 1996-1997 Microsoft Corporation
* Copyright (C) 1996-1997 Hewlett Packard
*
* history:
*   22-Nov-1996 <chriswil> created.
*
\*****************************************************************************/

#include "pch.h"

/*****************************************************************************\
* genGetCurDir
*
* Returns string indicating current-directory.
*
\*****************************************************************************/
LPTSTR genGetCurDir(VOID)
{
    DWORD  cbSize;
    LPTSTR lpszDir = NULL;


    cbSize = GetCurrentDirectory(0, NULL);

    if (cbSize && (lpszDir = (LPTSTR)genGAlloc((cbSize * sizeof(TCHAR)))))
        GetCurrentDirectory(cbSize, lpszDir);

    return lpszDir;
}


/*****************************************************************************\
* genGetWinDir
*
* Returns string indicating the windows-directory.
*
\*****************************************************************************/
LPTSTR genGetWinDir(VOID)
{
    DWORD  cbSize;
    LPTSTR lpszDir = NULL;


    cbSize = GetWindowsDirectory(NULL, 0);

    if (cbSize && (lpszDir = (LPTSTR)genGAlloc((cbSize * sizeof(TCHAR)))))
        GetWindowsDirectory(lpszDir, cbSize);

    return lpszDir;
}


/*****************************************************************************\
* genBuildFileName
*
* Takes path, name, extension strings and builds a fully-qualified
* string representing the file.  This can also be used to build other
* names.
*
\*****************************************************************************/
LPTSTR genBuildFileName(
    LPCTSTR lpszPath,
    LPCTSTR lpszName,
    LPCTSTR lpszExt)
{
    DWORD  cch;
    LPTSTR lpszFull;


    // Calculate the size necessary to hold the full-path filename.
    //
    cch  = lstrlen(g_szBkSlash);
    cch += (lpszPath ? lstrlen(lpszPath) : 0);
    cch += (lpszName ? lstrlen(lpszName) : 0);
    cch += (lpszExt  ? lstrlen(lpszExt)  : 0);


    if (lpszFull = (LPTSTR)genGAlloc(((cch + 1) * sizeof(TCHAR)))) {

        if (lpszPath) {

            if (lpszExt)
                cch = wsprintf(lpszFull, TEXT("%s\\%s%s"), lpszPath, lpszName, lpszExt);
            else
                cch = wsprintf(lpszFull, TEXT("%s\\%s"), lpszPath, lpszName);

        } else {

            if (lpszExt)
                cch = wsprintf(lpszFull, TEXT("%s%s"), lpszName, lpszExt);
            else
                cch = wsprintf(lpszFull, TEXT("%s"), lpszName);
        }
    }

    return lpszFull;
}


/*****************************************************************************\
* genFindCharDiff
*
* This routine returns a pointer to the location in DST, where the characters
* cease to match.
*
\*****************************************************************************/
LPTSTR genFindCharDiff(
    LPTSTR lpszDst,
    LPTSTR lpszSrc)
{
    LPTSTR lpszCS;
    LPTSTR lpszCD;


    CharLower(lpszSrc);
    CharLower(lpszDst);


    lpszCS = lpszSrc;
    lpszCD = lpszDst;

    while (*lpszCS == *lpszCD) {
        lpszCD++;
        lpszCS++;
    }

    return (*lpszCD ? lpszCD : NULL);
}


/*****************************************************************************\
* genFindChar
*
* Searches for the first occurence of (cch) in a string.
*
\*****************************************************************************/
LPTSTR genFindChar(
    LPTSTR lpszStr,
    TCHAR  cch)
{
    if (lpszStr) {

        while ((*lpszStr != cch) && (*lpszStr != TEXT('\0')))
            lpszStr++;

        if (((cch != TEXT('\0')) && (*lpszStr != TEXT('\0'))) || (cch == TEXT('\0')))
            return lpszStr;
    }

    return NULL;
}


/*****************************************************************************\
* genFindRChar
*
* Searches for the first occurence of (cch) in a string in reverse order.
*
\*****************************************************************************/
LPTSTR genFindRChar(
    LPTSTR lpszStr,
    TCHAR  cch)
{
    int nLimit;

    if (nLimit = lstrlen(lpszStr)) {

        lpszStr += nLimit;

        while ((*lpszStr != cch) && nLimit--)
            lpszStr--;

        if (nLimit >= 0)
            return lpszStr;
    }

    return NULL;
}


/*****************************************************************************\
* genWCFromMB
*
* This routine returns a buffer of wide-character representation of a
* ansi string.  The caller is responsible for freeing this pointer returned
* by this function.
*
\*****************************************************************************/
LPWSTR genWCFromMB(
    LPCSTR lpszStr)
{
    DWORD  cbSize;
    LPWSTR lpwszBuf = NULL;


    cbSize = genMBtoWC(NULL, lpszStr, 0);

    if (cbSize && (lpwszBuf = (LPWSTR)genGAlloc(cbSize)))
        genMBtoWC(lpwszBuf, lpszStr, cbSize);

    return lpwszBuf;
}


/*****************************************************************************\
* genTCFromMB
*
* This routine returns a buffer of tchar representation of a
* ansi string.  The caller is responsible for freeing this pointer returned
* by this function.
*
\*****************************************************************************/
LPTSTR genTCFromMB(
    LPCSTR lpszStr)
{

#ifdef UNICODE

    return genWCFromMB(lpszStr);

#else

    return genGAllocStr(lpszStr);

#endif
}

/*****************************************************************************\
* genTCFromWC
*
* This routine returns a buffer of tchar representation of a
* wide string.  The caller is responsible for freeing this pointer returned
* by this function.
*
\*****************************************************************************/
LPTSTR genTCFromWC(
    LPCWSTR lpszwStr)
{

#ifdef UNICODE
    
    return genGAllocStr(lpszwStr);

#else

    return genMBFromWC(lpszwStr);
    
#endif
}


/*****************************************************************************\
* genMBFromWC
*
* This routine returns a buffer of byte-character representation of a
* wide-char string.  The caller is responsible for freeing this pointer
* returned by this function.
*
\*****************************************************************************/
LPSTR genMBFromWC(
    LPCWSTR lpwszStr)
{
    DWORD cbSize;
    LPSTR lpszBuf = NULL;


    cbSize = genWCtoMB(NULL, lpwszStr, 0);

    if (cbSize && (lpszBuf = (LPSTR)genGAlloc(cbSize)))
        genWCtoMB(lpszBuf, lpwszStr, cbSize);

    return lpszBuf;
}


/*****************************************************************************\
* genMBFromTC
*
* This routine returns a buffer of byte-character representation of a
* tchar string.  The caller is responsible for freeing this pointer
* returned by this function.
*
\*****************************************************************************/
LPSTR genMBFromTC(
    LPCTSTR lpszStr)
{

#ifdef UNICODE

    return genMBFromWC(lpszStr);

#else

    return genGAllocStr(lpszStr);

#endif

}


/*****************************************************************************\
* genItoA
*
* Convert integer to string.
*
\*****************************************************************************/
LPTSTR genItoA(
    int nVal)
{
    DWORD  cch = 0;
    LPTSTR lpszVal;

    if (lpszVal = (LPTSTR)genGAlloc(INF_MIN_BUFFER))
        cch = wsprintf(lpszVal, TEXT("%d"), nVal);

    SPLASSERT((cch < INF_MIN_BUFFER));

    return lpszVal;
}


/*****************************************************************************\
* Client Platform Table.
*
* This table describes the various platforms/architectures.  By refering
* to the index into the table, various platform information can be
* obtained.
*
* Members are as follows:
* a) CAB string.  Used to denote the architecture in the cab-name.
* b) Environment string.  Used to denote printer-environment.
* c) Platform string.  Used for path override in setup api's.
* d) Architecture value.  Used to denote platform of client.
*
\*****************************************************************************/
static PLTINFO s_PltTable[] = {

    g_szCabX86, g_szEnvX86, g_szPltX86, PROCESSOR_ARCHITECTURE_INTEL,   // IDX_X86
    g_szCabMIP, g_szEnvMIP, g_szPltMIP, PROCESSOR_ARCHITECTURE_MIPS,    // IDX_MIP
    g_szCabAXP, g_szEnvAXP, g_szPltAXP, PROCESSOR_ARCHITECTURE_ALPHA,   // IDX_AXP
    g_szCabPPC, g_szEnvPPC, g_szPltPPC, PROCESSOR_ARCHITECTURE_PPC,     // IDX_PPC
    g_szCabW9X, g_szEnvW9X, g_szPltW9X, PROCESSOR_ARCHITECTURE_INTEL,   // IDX_W9X
    g_szCabI64, g_szEnvI64, g_szPltI64, PROCESSOR_ARCHITECTURE_IA64,    // IDX_I64
    g_szCabAMD64, g_szEnvAMD64, g_szPltAMD64, PROCESSOR_ARCHITECTURE_AMD64 // IDX_AMD64
};


/*****************************************************************************\
* Client Version Table.
*
* This table describes the spooler-versions which the client can request
* drivers for.
*
\*****************************************************************************/
static LPCTSTR s_VerTable[] = {

    TEXT("\\0"),    // Win NT 3.1  - IDX_SPLVER_0
    TEXT("\\1"),    // Win NT 3.51 - IDX_SPLVER_1
    TEXT("\\2"),    // Win NT 4.0  - IDX_SPLVER_2
    TEXT("\\3")     // Win NT 5.0  - IDX_SPLVER_3
};


/*****************************************************************************\
* genIdxCliPlatform
*
* This routine returns a platform-index into the s_PltTable.  The caller
* can use this index to refer to platform specific information about the
* client.
*
\*****************************************************************************/
DWORD genIdxCliPlatform(
    DWORD dwCliInfo)
{
    DWORD idx;
    DWORD cEnv;
    WORD  wArch;


    // If the platform is win9X, then set the index appropriately.  Otherwise,
    // continue on to determine the correct architecture for the NT case.
    //
    if (webGetOSPlatform(dwCliInfo) == VER_PLATFORM_WIN32_WINDOWS)
        return IDX_W9X;


    // Otherwise, the client is an NT platform.
    //
    cEnv  = sizeof(s_PltTable) / sizeof(s_PltTable[0]);
    wArch = webGetOSArch(dwCliInfo);


    // Look for matching client-info for the NT case.  The architecture
    // values will match up in this case.
    //
    for (idx = 0; idx < cEnv; idx++) {

        if (wArch == s_PltTable[idx].wArch)
            return idx;
    }

    return IDX_UNKNOWN;

}

/*****************************************************************************\
* genStrCliCab
*
* This routine returns a static-string representing the client-cabname.
*
\*****************************************************************************/
LPCTSTR genStrCliCab(
    DWORD idxPlt)
{
    return (idxPlt < (sizeof(s_PltTable) / sizeof(s_PltTable[0])) ? s_PltTable[idxPlt].lpszCab : NULL);
}


/*****************************************************************************\
* genStrCliEnvironment
*
* This routine returns a static-string representing the client-platform.  This
* string is used by the spooler API calls to specify environment.
*
\*****************************************************************************/
LPCTSTR genStrCliEnvironment(
    DWORD idxPlt)
{
    return (idxPlt < (sizeof(s_PltTable) / sizeof(s_PltTable[0])) ? s_PltTable[idxPlt].lpszEnv : NULL);
}


/*****************************************************************************\
* genStrCliOverride
*
* This routines returns a static-string representing the client-path-override
* for the setup API.
*
\*****************************************************************************/
LPCTSTR genStrCliOverride(
    DWORD idxPlt)
{
    return (idxPlt < (sizeof(s_PltTable) / sizeof(s_PltTable[0])) ? s_PltTable[idxPlt].lpszPlt : NULL);
}


/*****************************************************************************\
* genValCliArchitecture
*
* Returns the architecture platform of the client.
*
\*****************************************************************************/
WORD genValCliArchitecture(
    DWORD idxPlt)
{
    return (idxPlt < (sizeof(s_PltTable) / sizeof(s_PltTable[0])) ? s_PltTable[idxPlt].wArch : PROCESSOR_ARCHITECTURE_UNKNOWN);
}


/*****************************************************************************\
* genValSvrArchitecture
*
* Returns the architecture platform of the server.  The current architecture
* running this dll.
*
\*****************************************************************************/
WORD genValSvrArchitecture(VOID)
{
    DWORD idxEnv;

#if defined(_X86_)

    idxEnv = IDX_X86;

#elif defined(_AMD64_)

    idxEnv = IDX_AMD64;

#elif defined(_IA64_)

    idxEnv = IDX_I64;
    
#endif

    return genValCliArchitecture(idxEnv);
}


/*****************************************************************************\
* genIdxCliVersion
*
* This routine returns an index into the s_VerTable.  The caller can refer
* to this index for the client-version information.
*
\*****************************************************************************/
DWORD genIdxCliVersion(
    DWORD dwCliInfo)
{
    DWORD dwPlt = webGetOSPlatform(dwCliInfo);
    DWORD dwMaj = webGetOSMajorVer(dwCliInfo);
    DWORD dwMin = webGetOSMinorVer(dwCliInfo);


    if (dwMaj == 5)
        return IDX_SPLVER_3;

    if ((dwMaj == 4) && (dwPlt == VER_PLATFORM_WIN32_NT))
        return IDX_SPLVER_2;

    if ((dwMaj == 3) && (dwMin == 51))
        return IDX_SPLVER_1;

    if ((dwMaj == 4) && (dwPlt == VER_PLATFORM_WIN32_WINDOWS))
        return IDX_SPLVER_0;

    if ((dwMaj == 3) && (dwMin == 1))
        return IDX_SPLVER_0;

    return IDX_UNKNOWN;
}


/*****************************************************************************\
* genStrCliVersion
*
* Returns a string representing the spooler-version directory.  This is
* the relative directory off the system32\spool\drivers\*\ path that contains
* the drivers.
*
\*****************************************************************************/
LPCTSTR genStrCliVersion(
    DWORD idxVer)
{
    return (idxVer < (sizeof(s_VerTable) / sizeof(s_VerTable[0])) ? s_VerTable[idxVer] : NULL);
}


/*****************************************************************************\
* genIdxFromStrVersion
*
* Returns an index that matches the client-version-string.
*
\*****************************************************************************/
DWORD genIdxFromStrVersion(
    LPCTSTR lpszVer)
{
    DWORD idx;
    DWORD cVer;


    cVer  = sizeof(s_VerTable) / sizeof(s_VerTable[0]);

    for (idx = 0; idx < cVer; idx++) {

        if (lstrcmpi(lpszVer, s_VerTable[idx]) == 0)
            return idx;
    }

    return IDX_UNKNOWN;
}


/*****************************************************************************\
* genUpdIPAddr
*
* Updates the registry with the current IP-Addr of this machine.  If there
* is already an entry in the registry and it's different than the one
* currently established for the machine, then we return FALSE, and update
* the entry.
*
\*****************************************************************************/
BOOL genUpdIPAddr(VOID)
{
    HKEY    hKey;
    LRESULT lRet;
    LPSTR   lpszCmp;
    DWORD   cbData;
    DWORD   dwIpCmp;
    DWORD   dwIpReg;
    DWORD   dwVal;
    BOOL    bRet = TRUE;


    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                        g_szPrtReg,
                        0,
                        KEY_READ | KEY_WRITE,
                        &hKey);

    if (lRet == ERROR_SUCCESS) {

        if (lpszCmp = genMBFromWC(g_szHttpServerName)) {

            // Get the IP-Addr associated with this machine.
            //
            dwIpCmp = GetIPAddr(lpszCmp);


            // Setup our registry-information so get/set a value.
            //
            dwVal  = REG_DWORD;
            cbData = sizeof(DWORD);


            // Get what we already have stored there.  If no value exists,
            // the write it out.
            //
            lRet = RegQueryValueEx(hKey,
                                   g_szIpAddr,
                                   NULL,
                                   &dwVal,
                                   (LPBYTE)&dwIpReg,
                                   &cbData);

            if ((lRet != ERROR_SUCCESS) || (dwIpReg != dwIpCmp)) {

                bRet = FALSE;

                RegSetValueEx(hKey,
                              g_szIpAddr,
                              0,
                              REG_DWORD,
                              (LPBYTE)&dwIpCmp,
                              cbData);
            }

            genGFree(lpszCmp, genGSize(lpszCmp));
        }

        RegCloseKey(hKey);
    }

    return bRet;
}


/*****************************************************************************\
* genFrnName
*
* Returns a cluster-capable friendly-name.
*
\*****************************************************************************/
LPTSTR genFrnName(
    LPCTSTR lpszFrnName)
{
    DWORD  cbSize;
    LPTSTR lpszName = NULL;


    // Calc size for friendly-name.
    //
    cbSize = lstrlen(lpszFrnName) + lstrlen(g_szPrintServerName) + 6;


    // Build it.
    //
    if (lpszName = (LPTSTR)genGAlloc(cbSize * sizeof(TCHAR)))
        wsprintf(lpszName, TEXT("\\\\%s\\%s"), g_szPrintServerName, lpszFrnName);

    return lpszName;
}


/*****************************************************************************\
* genChkSum (Local Routine)
*
* This routine checksums a string into a WORD value.
*
\*****************************************************************************/

#define CRC_HI(wHi) (((wHi << 1) | (wHi & 0x8000)) ? 0x0001 : 0)
#define CRC_LO(wLo) (((wLo >> 1) | (wLo & 0x0001)) ? 0x8000 : 0)

WORD genChkSum(
    LPCTSTR lpszStr)
{
    WORD  wMask;
    DWORD idx;
    DWORD cLoop;
    DWORD cbStr;
    WORD  wHi     = 0;
    WORD  wLo     = 0;
    WORD  wChkSum = 0;


    if (lpszStr && (cbStr = lstrlen(lpszStr))) {

        // Loop through the bytes (in WORD increments).  This is an
        // optimized method in cyclying through bits.
        //
        cLoop = (cbStr / sizeof(WORD));

        for (idx = 0, wLo = 0, wHi = 0, wChkSum = 0; idx < cLoop; idx++) {

            wChkSum += *(((PWORD)lpszStr) + idx);

            wHi = CRC_HI(wHi) ^ wChkSum;
            wLo = CRC_LO(wLo) ^ wChkSum;
        }


        // If there's any extra bytes left over, then include that
        // in the checksum.  Mask off any bytes that should be
        // excluded from the checksum.
        //
        if (cbStr & 3) {

            wMask = ((WORD)-1 >> ((sizeof(WORD) - (cbStr & 3)) * 8));

            wChkSum += ((*((PWORD)lpszStr + cLoop)) & wMask);

            wHi = CRC_HI(wHi) ^ wChkSum;
            wLo = CRC_LO(wLo) ^ wChkSum;
        }
    }

    return (wChkSum + wHi + wLo);
}