Ejemplo n.º 1
0
/**
 * Make sure the Id and Revision keywords are expanded.
 *
 * @returns false - the state carries these kinds of changes.
 * @param   pState              The rewriter state.
 * @param   pIn                 The input stream.
 * @param   pOut                The output stream.
 * @param   pSettings           The settings.
 */
bool rewrite_SvnKeywords(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
{
    if (   !pSettings->fSetSvnKeywords
        || !ScmSvnIsInWorkingCopy(pState))
        return false;

    char *pszKeywords;
    int rc = ScmSvnQueryProperty(pState, "svn:keywords", &pszKeywords);
    if (    RT_SUCCESS(rc)
        && (   !strstr(pszKeywords, "Id") /** @todo need some function for finding a word in a string.  */
            || !strstr(pszKeywords, "Revision")) )
    {
        if (!strstr(pszKeywords, "Id") && !strstr(pszKeywords, "Revision"))
            rc = RTStrAAppend(&pszKeywords, " Id Revision");
        else if (!strstr(pszKeywords, "Id"))
            rc = RTStrAAppend(&pszKeywords, " Id");
        else
            rc = RTStrAAppend(&pszKeywords, " Revision");
        if (RT_SUCCESS(rc))
        {
            ScmVerbose(pState, 2, " * changing svn:keywords to '%s'\n", pszKeywords);
            rc = ScmSvnSetProperty(pState, "svn:keywords", pszKeywords);
            if (RT_FAILURE(rc))
                RTMsgError("ScmSvnSetProperty: %Rrc\n", rc); /** @todo error propagation here.. */
        }
        else
            RTMsgError("RTStrAppend: %Rrc\n", rc); /** @todo error propagation here.. */
        RTStrFree(pszKeywords);
    }
    else if (rc == VERR_NOT_FOUND)
    {
        ScmVerbose(pState, 2, " * setting svn:keywords to 'Id Revision'\n");
        rc = ScmSvnSetProperty(pState, "svn:keywords", "Id Revision");
        if (RT_FAILURE(rc))
            RTMsgError("ScmSvnSetProperty: %Rrc\n", rc); /** @todo error propagation here.. */
    }
    else if (RT_SUCCESS(rc))
        RTStrFree(pszKeywords);

    return false;
}
Ejemplo n.º 2
0
/**
 * Basic API checks.
 * We'll return if any of these fails.
 */
static void tst1(void)
{
    RTTestISub("Basics");
    char *psz;
    int rc = VINF_SUCCESS;

    /* RTStrAlloc */
    RTTESTI_CHECK(psz = RTStrAlloc(0));
    RTTESTI_CHECK(psz && !*psz);
    RTStrFree(psz);

    RTTESTI_CHECK(psz = RTStrAlloc(1));
    RTTESTI_CHECK(psz && !*psz);
    RTStrFree(psz);

    RTTESTI_CHECK(psz = RTStrAlloc(128));
    RTTESTI_CHECK(psz && !*psz);
    RTStrFree(psz);

    /* RTStrAllocEx */
    psz = (char*)"asdfasdf";
    RTTESTI_CHECK_RC(RTStrAllocEx(&psz, 0), VINF_SUCCESS);
    RTTESTI_CHECK(psz && !*psz);
    RTStrFree(psz);

    RTTESTI_CHECK_RC(RTStrAllocEx(&psz, 1), VINF_SUCCESS);
    RTTESTI_CHECK(psz && !*psz);
    RTStrFree(psz);

    RTTESTI_CHECK_RC(RTStrAllocEx(&psz, 128), VINF_SUCCESS);
    RTTESTI_CHECK(psz && !*psz);
    RTStrFree(psz);

    /* RTStrRealloc */
    psz = NULL;
    RTTESTI_CHECK_RC(RTStrRealloc(&psz, 10), VINF_SUCCESS);
    RTTESTI_CHECK(psz && !psz[0]);
    RTTESTI_CHECK(psz && !psz[9]);
    RTStrFree(psz);

    psz = NULL;
    RTTESTI_CHECK_RC(RTStrRealloc(&psz, 0), VINF_SUCCESS);
    RTTESTI_CHECK(!psz);

    psz = NULL;
    RTTESTI_CHECK_RC(RTStrRealloc(&psz, 128), VINF_SUCCESS);
    RTTESTI_CHECK(psz && !psz[0]);
    RTTESTI_CHECK(psz && !psz[127]);
    if (psz)
    {
        memset(psz, 'a', 127);
        RTTESTI_CHECK_RC(rc = RTStrRealloc(&psz, 160), VINF_SUCCESS);
        if (RT_SUCCESS(rc) && psz)
        {
            RTTESTI_CHECK(!psz[127]);
            RTTESTI_CHECK(!psz[159]);
            RTTESTI_CHECK(ASMMemIsAll8(psz, 127, 'a') == NULL);
            memset(psz, 'b', 159);

            RTTESTI_CHECK_RC(rc = RTStrRealloc(&psz, 79), VINF_SUCCESS);
            if (RT_SUCCESS(rc))
            {
                RTTESTI_CHECK(!psz[78]);
                RTTESTI_CHECK(ASMMemIsAll8(psz, 78, 'b') == NULL);

                RTTESTI_CHECK_RC(rc = RTStrRealloc(&psz, 0), VINF_SUCCESS);
                RTTESTI_CHECK(!psz);
            }
        }
    }
    RTStrFree(psz);

    /* RTStrDup */
    RTTESTI_CHECK(psz = RTStrDup(""));
    RTTESTI_CHECK(psz && *psz == '\0');
    RTStrFree(psz);

    RTTESTI_CHECK(psz = RTStrDup("abcdefghijklmnop"));
    RTTESTI_CHECK(!RTStrCmp(psz, "abcdefghijklmnop"));
    RTStrFree(psz);

    /* RTStrDupEx */
    psz = NULL;
    RTTESTI_CHECK_RC(RTStrDupEx(&psz, ""), VINF_SUCCESS);
    RTTESTI_CHECK(RT_FAILURE(rc) || *psz == '\0');
    if (RT_SUCCESS(rc))
        RTStrFree(psz);

    psz = (char*)"asdfasdfasdfasdf";
    RTTESTI_CHECK_RC(rc = RTStrDupEx(&psz, "abcdefghijklmnop"), VINF_SUCCESS);
    RTTESTI_CHECK(RT_FAILURE(rc) || !RTStrCmp(psz, "abcdefghijklmnop"));
    if (RT_SUCCESS(rc))
        RTStrFree(psz);

    /* RTStrDupN */
    RTTESTI_CHECK(psz = RTStrDupN("abcdefg", 3));
    RTTESTI_CHECK(!RTStrCmp(psz, "abc"));
    RTStrFree(psz);

    RTTESTI_CHECK(psz = RTStrDupN("abc", 100000));
    RTTESTI_CHECK(!RTStrCmp(psz, "abc"));
    RTStrFree(psz);

    RTTESTI_CHECK(psz = RTStrDupN("abc", 0));
    RTTESTI_CHECK(psz && *psz == '\0');
    RTStrFree(psz);

    /* RTStrAAppend */
    RTTESTI_CHECK(psz = RTStrDup("abc"));
    RTTESTI_CHECK_RC(RTStrAAppend(&psz, "def"), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, "abcdef"));
    RTStrFree(psz);

    RTTESTI_CHECK(psz = RTStrDup("abc"));
    RTTESTI_CHECK_RC(RTStrAAppend(&psz, ""), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, "abc"));
    RTTESTI_CHECK_RC(RTStrAAppend(&psz, NULL), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, "abc"));
    RTStrFree(psz);

    psz = NULL;
    RTTESTI_CHECK_RC(RTStrAAppend(&psz, "xyz"), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, "xyz"));
    RTStrFree(psz);

    /* RTStrAAppendN */
    RTTESTI_CHECK(psz = RTStrDup("abc"));
    RTTESTI_CHECK_RC(RTStrAAppendN(&psz, "def", 1), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, "abcd"));
    RTStrFree(psz);

    RTTESTI_CHECK(psz = RTStrDup("abc"));
    RTTESTI_CHECK_RC(RTStrAAppendN(&psz, "", 0), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, "abc"));
    RTTESTI_CHECK_RC(RTStrAAppendN(&psz, "", RTSTR_MAX), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, "abc"));
    RTTESTI_CHECK_RC(RTStrAAppendN(&psz, NULL, 0), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, "abc"));
    RTStrFree(psz);

    psz = NULL;
    RTTESTI_CHECK_RC(RTStrAAppendN(&psz, "abc", 2), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, "ab"));
    RTTESTI_CHECK_RC(RTStrAAppendN(&psz, "cdefghijklm", 1), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, "abc"));
    RTTESTI_CHECK_RC(RTStrAAppendN(&psz, "defghijklm", RTSTR_MAX), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, "abcdefghijklm"));
    RTStrFree(psz);

    /* RTStrAAppendExN / RTStrAAppendExNV */
    psz = NULL;
    RTTESTI_CHECK_RC(RTStrAAppendExN(&psz, 5, "a", (size_t)1, "bc", (size_t)1, "cdefg", RTSTR_MAX, "hijkl", (size_t)2, "jklmnopqrstuvwxyz", RTSTR_MAX), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, "abcdefghijklmnopqrstuvwxyz"));
    RTTESTI_CHECK_RC(RTStrAAppendExN(&psz, 0), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, "abcdefghijklmnopqrstuvwxyz"));
    RTTESTI_CHECK_RC(RTStrAAppendExN(&psz, 2, NULL, (size_t)0, "", (size_t)0), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, "abcdefghijklmnopqrstuvwxyz"));
    RTTESTI_CHECK_RC(RTStrAAppendExN(&psz, 1, "-", (size_t)1), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, "abcdefghijklmnopqrstuvwxyz-"));
    RTStrFree(psz);

    /* RTStrATruncate */
    psz = NULL;
    RTTESTI_CHECK_RC(RTStrATruncate(&psz, 0), VINF_SUCCESS);
    RTTESTI_CHECK(!psz);

    RTTESTI_CHECK(psz = RTStrDup(""));
    RTTESTI_CHECK_RC(RTStrATruncate(&psz, 0), VINF_SUCCESS);
    RTStrFree(psz);

    RTTESTI_CHECK(psz = RTStrDup("1234567890"));
    RTTESTI_CHECK_RC(RTStrATruncate(&psz, 5), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, "12345"));
    RTStrFree(psz);

    psz = NULL;
    for (uint32_t i = 0; i < 128; i++)
        RTTESTI_CHECK_RC_RETV(RTStrAAppend(&psz, "abcdefghijklmnopqrstuvwxyz"), VINF_SUCCESS);
    RTTESTI_CHECK_RC(RTStrATruncate(&psz, sizeof("abcdefghijklmnopqrstuvwxyz") - 1), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, "abcdefghijklmnopqrstuvwxyz"));
    RTTESTI_CHECK_RC(RTStrATruncate(&psz, 6), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, "abcdef"));
    RTTESTI_CHECK_RC(RTStrATruncate(&psz, 1), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, "a"));
    RTTESTI_CHECK_RC(RTStrATruncate(&psz, 0), VINF_SUCCESS);
    RTTESTI_CHECK(!RTStrCmp(psz, ""));
    RTStrFree(psz);

}
RTDECL(int) RTEnvClone(PRTENV pEnv, RTENV EnvToClone)
{
    /*
     * Validate input and figure out how many variable to clone and where to get them.
     */
    bool fCaseSensitive = true;
    bool fPutEnvBlock   = false;
    size_t cVars;
    const char * const *papszEnv;
#ifdef RTENV_HAVE_WENVIRON
    PCRTUTF16 const * papwszEnv;
#endif
    PRTENVINTERNAL pIntEnvToClone;
    AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
    if (EnvToClone == RTENV_DEFAULT)
    {
        cVars = 0;
        pIntEnvToClone = NULL;
#ifdef RTENV_HAVE_WENVIRON
        papszEnv  = NULL;
        papwszEnv = (PCRTUTF16 * const)_wenviron;
        if (!papwszEnv)
        {
            _wgetenv(L"Path"); /* Force the CRT to initalize it. */
            papwszEnv = (PCRTUTF16 * const)_wenviron;
        }
        if (papwszEnv)
            while (papwszEnv[cVars])
                cVars++;
#else
        papszEnv = rtEnvDefault();
        if (papszEnv)
            while (papszEnv[cVars])
                cVars++;
#endif

#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
        /* DOS systems was case insensitive.  A prime example is the 'Path'
           variable on windows which turns into the 'PATH' variable. */
        fCaseSensitive = false;
#endif
    }
    else
    {
        pIntEnvToClone = EnvToClone;
        AssertPtrReturn(pIntEnvToClone, VERR_INVALID_HANDLE);
        AssertReturn(pIntEnvToClone->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
        RTENV_LOCK(pIntEnvToClone);

        fPutEnvBlock = pIntEnvToClone->fPutEnvBlock;
        papszEnv = pIntEnvToClone->papszEnv;
        cVars = pIntEnvToClone->cVars;
    }

    /*
     * Create the duplicate.
     */
    PRTENVINTERNAL pIntEnv;
    int rc = rtEnvCreate(&pIntEnv, cVars + 1 /* NULL */, fCaseSensitive, fPutEnvBlock);
    if (RT_SUCCESS(rc))
    {
        pIntEnv->cVars = cVars;
        pIntEnv->papszEnv[pIntEnv->cVars] = NULL;
        if (EnvToClone == RTENV_DEFAULT)
        {
            /* ASSUMES the default environment is in the current codepage. */
            size_t  iDst = 0;
            for (size_t iSrc = 0; iSrc < cVars; iSrc++)
            {
#ifdef RTENV_HAVE_WENVIRON
                int rc2 = RTUtf16ToUtf8(papwszEnv[iSrc], &pIntEnv->papszEnv[iDst]);
#else
                int rc2 = RTStrCurrentCPToUtf8(&pIntEnv->papszEnv[iDst], papszEnv[iSrc]);
#endif
                if (RT_SUCCESS(rc2))
                {
                    /* Make sure it contains an '='. */
                    iDst++;
                    if (strchr(pIntEnv->papszEnv[iDst - 1], '='))
                        continue;
                    rc2 = RTStrAAppend(&pIntEnv->papszEnv[iDst - 1], "=");
                    if (RT_SUCCESS(rc2))
                        continue;
                }
                else if (rc2 == VERR_NO_TRANSLATION)
                {
                    rc = VWRN_ENV_NOT_FULLY_TRANSLATED;
                    continue;
                }

                /* failed fatally. */
                pIntEnv->cVars = iDst;
                RTEnvDestroy(pIntEnv);
                return rc2;
            }
            pIntEnv->cVars = iDst;
        }
        else
        {
            for (size_t iVar = 0; iVar < cVars; iVar++)
            {
                char *pszVar = RTStrDup(papszEnv[iVar]);
                if (RT_UNLIKELY(!pszVar))
                {
                    RTENV_UNLOCK(pIntEnvToClone);

                    pIntEnv->cVars = iVar;
                    RTEnvDestroy(pIntEnv);
                    return VERR_NO_STR_MEMORY;
                }
                pIntEnv->papszEnv[iVar] = pszVar;
            }
        }

        /* done */
        *pEnv = pIntEnv;
    }

    if (pIntEnvToClone)
        RTENV_UNLOCK(pIntEnvToClone);
    return rc;
}
/**
 * Retrieves the currently logged in users and stores their names along with the
 * user count.
 *
 * @returns VBox status code.
 * @param   ppszUserList    Where to store the user list (separated by commas).
 *                          Must be freed with RTStrFree().
 * @param   pcUsersInList   Where to store the number of users in the list.
 */
int VBoxServiceVMInfoWinWriteUsers(char **ppszUserList, uint32_t *pcUsersInList)
{
    PLUID       paSessions = NULL;
    ULONG       cSessions = 0;

    /* This function can report stale or orphaned interactive logon sessions
       of already logged off users (especially in Windows 2000). */
    NTSTATUS rcNt = LsaEnumerateLogonSessions(&cSessions, &paSessions);
    if (rcNt != STATUS_SUCCESS)
    {
        ULONG ulError = LsaNtStatusToWinError(rcNt);
        switch (ulError)
        {
            case ERROR_NOT_ENOUGH_MEMORY:
                VBoxServiceError("Not enough memory to enumerate logon sessions!\n");
                break;

            case ERROR_SHUTDOWN_IN_PROGRESS:
                /* If we're about to shutdown when we were in the middle of enumerating the logon
                 * sessions, skip the error to not confuse the user with an unnecessary log message. */
                VBoxServiceVerbose(3, "Shutdown in progress ...\n");
                ulError = ERROR_SUCCESS;
                break;

            default:
                VBoxServiceError("LsaEnumerate failed with error %u\n", ulError);
                break;
        }

        return RTErrConvertFromWin32(ulError);
    }
    VBoxServiceVerbose(3, "Found %ld sessions\n", cSessions);

    PVBOXSERVICEVMINFOPROC  paProcs;
    DWORD                   cProcs;
    int rc = VBoxServiceVMInfoWinProcessesEnumerate(&paProcs, &cProcs);
    if (RT_FAILURE(rc))
    {
        if (rc == VERR_NO_MEMORY)
            VBoxServiceError("Not enough memory to enumerate processes\n");
        else
            VBoxServiceError("Failed to enumerate processes, rc=%Rrc\n", rc);
    }
    else
    {
        PVBOXSERVICEVMINFOUSER pUserInfo;
        pUserInfo = (PVBOXSERVICEVMINFOUSER)RTMemAllocZ(cSessions * sizeof(VBOXSERVICEVMINFOUSER) + 1);
        if (!pUserInfo)
            VBoxServiceError("Not enough memory to store enumerated users!\n");
        else
        {
            ULONG cUniqueUsers = 0;
            for (ULONG i = 0; i < cSessions; i++)
            {
                VBoxServiceVerbose(3, "Handling session %u\n", i);

                VBOXSERVICEVMINFOUSER UserInfo;
                if (VBoxServiceVMInfoWinIsLoggedIn(&UserInfo, &paSessions[i]))
                {
                    VBoxServiceVerbose(4, "Handling user=%ls, domain=%ls, package=%ls\n",
                                       UserInfo.wszUser, UserInfo.wszLogonDomain, UserInfo.wszAuthenticationPackage);

                    /* Retrieve assigned processes of current session. */
                    ULONG ulSession;
                    uint32_t cSessionProcs = VBoxServiceVMInfoWinSessionHasProcesses(&paSessions[i], paProcs, cProcs, &ulSession);
                    /* Don't return here when current session does not have assigned processes
                     * anymore -- in that case we have to search through the unique users list below
                     * and see if got a stale user/session entry. */

                    bool fFoundUser = false;
                    for (ULONG i = 0; i < cUniqueUsers; i++)
                    {
                        if (   !wcscmp(UserInfo.wszUser, pUserInfo[i].wszUser)
                            && !wcscmp(UserInfo.wszLogonDomain, pUserInfo[i].wszLogonDomain)
                            && !wcscmp(UserInfo.wszAuthenticationPackage, pUserInfo[i].wszAuthenticationPackage)
                            && cSessionProcs)
                        {
                            /*
                             * Only respect the highest session for the current user.
                             */
                            if (ulSession > pUserInfo[i].ulSession)
                            {
                                VBoxServiceVerbose(4, "Updating user=%ls to %u processes (last session: %u)\n",
                                                   UserInfo.wszUser, cSessionProcs, ulSession);

                                pUserInfo[i].ulNumProcs = cSessionProcs;
                                pUserInfo[i].ulSession  = ulSession;

                                if (!cSessionProcs)
                                    VBoxServiceVerbose(3, "Stale session for user=%ls detected! Old processes: %u, new: %u\n",
                                                       pUserInfo[i].wszUser, pUserInfo[i].ulNumProcs, cSessionProcs);
                            }
                            /* There can be multiple session objects using the same session ID for the
                             * current user -- so when we got the same session again just add the found
                             * processes to it. */
                            else if (pUserInfo[i].ulSession == ulSession)
                            {
                                VBoxServiceVerbose(4, "Adding %u processes to user=%ls (session %u)\n",
                                                   cSessionProcs, UserInfo.wszUser, ulSession);

                                pUserInfo[i].ulNumProcs += cSessionProcs;
                                pUserInfo[i].ulSession   = ulSession;
                            }

                            fFoundUser = true;
                            break;
                        }
                    }

                    if (!fFoundUser)
                    {
                        VBoxServiceVerbose(4, "Adding new user=%ls (session %u) with %u processes\n",
                                           UserInfo.wszUser, ulSession, cSessionProcs);

                        memcpy(&pUserInfo[cUniqueUsers], &UserInfo, sizeof(VBOXSERVICEVMINFOUSER));
                        pUserInfo[cUniqueUsers].ulNumProcs = cSessionProcs;
                        pUserInfo[cUniqueUsers].ulSession  = ulSession;
                        cUniqueUsers++;
                        Assert(cUniqueUsers <= cSessions);
                    }
                }
            }

            VBoxServiceVerbose(3, "Found %u unique logged-in user(s)\n",
                               cUniqueUsers);

            *pcUsersInList = 0;
            for (ULONG i = 0; i < cUniqueUsers; i++)
            {
                if (pUserInfo[i].ulNumProcs)
                {
                    VBoxServiceVerbose(3, "User %ls has %ld processes (session %u)\n",
                                       pUserInfo[i].wszUser, pUserInfo[i].ulNumProcs, pUserInfo[i].ulSession);

                    if (*pcUsersInList > 0)
                    {
                        rc = RTStrAAppend(ppszUserList, ",");
                        AssertRCBreakStmt(rc, RTStrFree(*ppszUserList));
                    }

                    *pcUsersInList += 1;

                    char *pszTemp;
                    int rc2 = RTUtf16ToUtf8(pUserInfo[i].wszUser, &pszTemp);
                    if (RT_SUCCESS(rc2))
                    {
                        rc = RTStrAAppend(ppszUserList, pszTemp);
                        RTMemFree(pszTemp);
                    }
                    else
                        rc = RTStrAAppend(ppszUserList, "<string-conversion-error>");
                    AssertRCBreakStmt(rc, RTStrFree(*ppszUserList));
                }
            }

            RTMemFree(pUserInfo);
        }
        VBoxServiceVMInfoWinProcessesFree(paProcs);
    }
    LsaFreeReturnBuffer(paSessions);
    return rc;
}
Ejemplo n.º 5
0
RTDECL(int) RTEnvCloneUtf16Block(PRTENV phEnv, PCRTUTF16 pwszzBlock, uint32_t fFlags)
{
    AssertPtrReturn(pwszzBlock, VERR_INVALID_POINTER);
    AssertReturn(!fFlags, VERR_INVALID_FLAGS);

    /*
     * Count the number of variables in the block.
     */
    uint32_t  cVars = 0;
    PCRTUTF16 pwsz  = pwszzBlock;
    while (*pwsz != '\0')
    {
        cVars++;
        pwsz += RTUtf16Len(pwsz) + 1;
        AssertReturn(cVars < _256K, VERR_OUT_OF_RANGE);
    }

    /*
     * Create the duplicate.
     */
    PRTENVINTERNAL pIntEnv;
    int rc = rtEnvCreate(&pIntEnv, cVars + 1 /* NULL */, false /*fCaseSensitive*/, false /*fPutEnvBlock*/);
    if (RT_SUCCESS(rc))
    {
        pIntEnv->cVars = cVars;
        pIntEnv->papszEnv[pIntEnv->cVars] = NULL;

        size_t iDst = 0;
        for (pwsz = pwszzBlock; *pwsz != '\0'; pwsz += RTUtf16Len(pwsz) + 1)
        {
            int rc2 = RTUtf16ToUtf8(pwsz, &pIntEnv->papszEnv[iDst]);
            if (RT_SUCCESS(rc2))
            {
                /* Make sure it contains an '='. */
                const char *pszEqual = strchr(pIntEnv->papszEnv[iDst], '=');
                if (!pszEqual)
                {
                    rc2 = RTStrAAppend(&pIntEnv->papszEnv[iDst], "=");
                    if (RT_SUCCESS(rc2))
                        pszEqual = strchr(pIntEnv->papszEnv[iDst], '=');

                }
                if (pszEqual)
                {
                    /* Check for duplicates, keep the last version. */
                    const char *pchVar        = pIntEnv->papszEnv[iDst];
                    size_t      cchVarNmAndEq = pszEqual - pchVar;
                    for (size_t iDst2 = 0; iDst2 < iDst; iDst2++)
                        if (pIntEnv->pfnCompare(pIntEnv->papszEnv[iDst2], pchVar, cchVarNmAndEq) == 0)
                        {
                            RTStrFree(pIntEnv->papszEnv[iDst2]);
                            pIntEnv->papszEnv[iDst2] = pIntEnv->papszEnv[iDst];
                            pIntEnv->papszEnv[iDst]  = NULL;
                            iDst--;
                            break;
                        }
                    iDst++;
                    continue;
                }
                iDst++;
            }

            /* failed fatally. */
            pIntEnv->cVars = iDst;
            RTEnvDestroy(pIntEnv);
            return rc2;
        }
        Assert(iDst <= pIntEnv->cVars);
        pIntEnv->cVars = iDst;

        /* done */
        *phEnv = pIntEnv;
    }
    return rc;
}