summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/generic/env-generic.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/generic/env-generic.cpp')
-rw-r--r--src/VBox/Runtime/generic/env-generic.cpp1409
1 files changed, 1409 insertions, 0 deletions
diff --git a/src/VBox/Runtime/generic/env-generic.cpp b/src/VBox/Runtime/generic/env-generic.cpp
new file mode 100644
index 00000000..f7892dce
--- /dev/null
+++ b/src/VBox/Runtime/generic/env-generic.cpp
@@ -0,0 +1,1409 @@
+/* $Id: env-generic.cpp $ */
+/** @file
+ * IPRT - Environment, Generic.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/env.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+#include <iprt/alloca.h>
+#include <iprt/err.h>
+#include <iprt/sort.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include "internal/magics.h"
+
+#ifdef RT_OS_WINDOWS
+# include <iprt/nt/nt.h>
+#else
+# include <stdlib.h>
+# if !defined(RT_OS_WINDOWS)
+# include <unistd.h>
+# endif
+# ifdef RT_OS_DARWIN
+# include <crt_externs.h>
+# endif
+# if defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) || defined(RT_OS_NETBSD) || defined(RT_OS_OPENBSD)
+RT_C_DECLS_BEGIN
+extern char **environ;
+RT_C_DECLS_END
+# endif
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The allocation granularity of the RTENVINTERNAL::papszEnv memory. */
+#define RTENV_GROW_SIZE 16
+
+/** Macro that unlocks the specified environment block. */
+#define RTENV_LOCK(pEnvInt) do { } while (0)
+/** Macro that unlocks the specified environment block. */
+#define RTENV_UNLOCK(pEnvInt) do { } while (0)
+
+/** @def RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API
+ * Indicates the RTEnv*Utf8 APIs are implemented. */
+#if defined(RT_OS_WINDOWS) || defined(DOXYGEN_RUNNING)
+# define RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API 1
+#endif
+
+/** @def RTENV_ALLOW_EQUAL_FIRST_IN_VAR
+ * Allows a variable to start with an '=' sign by default. This is used by
+ * windows to maintain CWDs of non-current drives.
+ * @note Not supported by _wputenv AFAIK. */
+#if defined(RT_OS_WINDOWS) || defined(DOXYGEN_RUNNING)
+# define RTENV_ALLOW_EQUAL_FIRST_IN_VAR 1
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The internal representation of a (non-default) environment.
+ */
+typedef struct RTENVINTERNAL
+{
+ /** Magic value . */
+ uint32_t u32Magic;
+ /** Set if this is a record of environment changes, putenv style. */
+ bool fPutEnvBlock;
+ /** Set if starting a variable with an equal sign is okay, clear if not okay
+ * (RTENV_CREATE_F_ALLOW_EQUAL_FIRST_IN_VAR). */
+ bool fFirstEqual;
+ /** Number of variables in the array.
+ * This does not include the terminating NULL entry. */
+ size_t cVars;
+ /** Capacity (allocated size) of the array.
+ * This includes space for the terminating NULL element (for compatibility
+ * with the C library), so that c <= cCapacity - 1. */
+ size_t cAllocated;
+ /** Array of environment variables.
+ * These are always in "NAME=VALUE" form, where the value can be empty. If
+ * fPutEnvBlock is set though, there will be "NAME" entries too for variables
+ * that need to be removed when merged with another environment block. */
+ char **papszEnv;
+ /** Array of environment variables in the process CP.
+ * This get (re-)constructed when RTEnvGetExecEnvP method is called. */
+ char **papszEnvOtherCP;
+
+ /** The compare function we're using. */
+ DECLCALLBACKMEMBER(int, pfnCompare,(const char *psz1, const char *psz2, size_t cchMax));
+} RTENVINTERNAL, *PRTENVINTERNAL;
+
+
+#ifndef RT_OS_WINDOWS
+/**
+ * Internal worker that resolves the pointer to the default
+ * process environment. (environ)
+ *
+ * @returns Pointer to the default environment.
+ * This may be NULL.
+ */
+static const char * const *rtEnvDefault(void)
+{
+# ifdef RT_OS_DARWIN
+ return *(_NSGetEnviron());
+# else
+ return environ;
+# endif
+}
+#endif
+
+
+/**
+ * Internal worker that creates an environment handle with a specified capacity.
+ *
+ * @returns IPRT status code.
+ * @param ppIntEnv Where to store the result.
+ * @param cAllocated The initial array size.
+ * @param fCaseSensitive Whether the environment block is case sensitive or
+ * not.
+ * @param fPutEnvBlock Indicates whether this is a special environment
+ * block that will be used to record change another
+ * block. We will keep unsets in putenv format, i.e.
+ * just the variable name without any equal sign.
+ * @param fFirstEqual The RTENV_CREATE_F_ALLOW_EQUAL_FIRST_IN_VAR value.
+ */
+static int rtEnvCreate(PRTENVINTERNAL *ppIntEnv, size_t cAllocated, bool fCaseSensitive, bool fPutEnvBlock, bool fFirstEqual)
+{
+ /*
+ * Allocate environment handle.
+ */
+ PRTENVINTERNAL pIntEnv = (PRTENVINTERNAL)RTMemAlloc(sizeof(*pIntEnv));
+ if (pIntEnv)
+ {
+ /*
+ * Pre-allocate the variable array.
+ */
+ pIntEnv->u32Magic = RTENV_MAGIC;
+ pIntEnv->fPutEnvBlock = fPutEnvBlock;
+ pIntEnv->fFirstEqual = fFirstEqual;
+ pIntEnv->pfnCompare = fCaseSensitive ? RTStrNCmp : RTStrNICmp;
+ pIntEnv->papszEnvOtherCP = NULL;
+ pIntEnv->cVars = 0;
+ pIntEnv->cAllocated = RT_ALIGN_Z(RT_MAX(cAllocated, RTENV_GROW_SIZE), RTENV_GROW_SIZE);
+ pIntEnv->papszEnv = (char **)RTMemAllocZ(sizeof(pIntEnv->papszEnv[0]) * pIntEnv->cAllocated);
+ if (pIntEnv->papszEnv)
+ {
+ *ppIntEnv = pIntEnv;
+ return VINF_SUCCESS;
+ }
+
+ RTMemFree(pIntEnv);
+ }
+
+ return VERR_NO_MEMORY;
+}
+
+
+RTDECL(int) RTEnvCreate(PRTENV pEnv)
+{
+ AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
+#ifdef RTENV_ALLOW_EQUAL_FIRST_IN_VAR
+ return rtEnvCreate(pEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, false /*fPutEnvBlock*/, true /*fFirstEqual*/);
+#else
+ return rtEnvCreate(pEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, false /*fPutEnvBlock*/, false/*fFirstEqual*/);
+#endif
+}
+RT_EXPORT_SYMBOL(RTEnvCreate);
+
+
+RTDECL(int) RTEnvCreateEx(PRTENV phEnv, uint32_t fFlags)
+{
+ AssertPtrReturn(phEnv, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTENV_CREATE_F_VALID_MASK), VERR_INVALID_FLAGS);
+ return rtEnvCreate(phEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, false /*fPutEnvBlock*/,
+ RT_BOOL(fFlags & RTENV_CREATE_F_ALLOW_EQUAL_FIRST_IN_VAR));
+}
+RT_EXPORT_SYMBOL(RTEnvCreateEx);
+
+
+RTDECL(int) RTEnvDestroy(RTENV Env)
+{
+ /*
+ * Ignore NIL_RTENV and validate input.
+ */
+ if ( Env == NIL_RTENV
+ || Env == RTENV_DEFAULT)
+ return VINF_SUCCESS;
+
+ PRTENVINTERNAL pIntEnv = Env;
+ AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the cleanup.
+ */
+ RTENV_LOCK(pIntEnv);
+ pIntEnv->u32Magic++;
+ size_t iVar = pIntEnv->cVars;
+ while (iVar-- > 0)
+ RTStrFree(pIntEnv->papszEnv[iVar]);
+ RTMemFree(pIntEnv->papszEnv);
+ pIntEnv->papszEnv = NULL;
+
+ if (pIntEnv->papszEnvOtherCP)
+ {
+ for (iVar = 0; pIntEnv->papszEnvOtherCP[iVar]; iVar++)
+ {
+ RTStrFree(pIntEnv->papszEnvOtherCP[iVar]);
+ pIntEnv->papszEnvOtherCP[iVar] = NULL;
+ }
+ RTMemFree(pIntEnv->papszEnvOtherCP);
+ pIntEnv->papszEnvOtherCP = NULL;
+ }
+
+ RTENV_UNLOCK(pIntEnv);
+ /*RTCritSectDelete(&pIntEnv->CritSect) */
+ RTMemFree(pIntEnv);
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTEnvDestroy);
+
+
+static int rtEnvCloneDefault(PRTENV phEnv)
+{
+#ifdef RTENV_ALLOW_EQUAL_FIRST_IN_VAR
+ bool const fFirstEqual = true;
+#else
+ bool const fFirstEqual = false;
+#endif
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * Lock the PEB, get the process environment.
+ *
+ * On older windows version GetEnviornmentStringsW will not copy the
+ * environment block, but return the pointer stored in the PEB. This
+ * should be safer wrt to concurrent changes.
+ */
+ PPEB pPeb = RTNtCurrentPeb();
+
+ RtlAcquirePebLock();
+
+ /* Count variables in the block: */
+ size_t cVars = 0;
+ PCRTUTF16 pwszzEnv = pPeb->ProcessParameters ? pPeb->ProcessParameters->Environment : NULL;
+ if (pwszzEnv)
+ {
+ PCRTUTF16 pwsz = pwszzEnv;
+ while (*pwsz)
+ {
+ cVars++;
+ pwsz += RTUtf16Len(pwsz) + 1;
+ }
+ }
+
+ PRTENVINTERNAL pIntEnv;
+ int rc = rtEnvCreate(&pIntEnv, cVars + 1 /* NULL */, false /*fCaseSensitive*/, false /*fPutEnvBlock*/, fFirstEqual);
+ if (RT_SUCCESS(rc))
+ {
+ size_t iDst;
+ for (iDst = 0; iDst < cVars && *pwszzEnv; iDst++, pwszzEnv += RTUtf16Len(pwszzEnv) + 1)
+ {
+ int rc2 = RTUtf16ToUtf8(pwszzEnv, &pIntEnv->papszEnv[iDst]);
+ if (RT_SUCCESS(rc2))
+ {
+ /* Make sure it contains an '='. */
+ if (strchr(pIntEnv->papszEnv[iDst], '='))
+ continue;
+ rc2 = RTStrAAppend(&pIntEnv->papszEnv[iDst], "=");
+ if (RT_SUCCESS(rc2))
+ continue;
+ }
+
+ /* failed fatally. */
+ pIntEnv->cVars = iDst + 1;
+ RtlReleasePebLock();
+ RTEnvDestroy(pIntEnv);
+ return rc2;
+ }
+
+ Assert(!*pwszzEnv); Assert(iDst == cVars);
+ pIntEnv->cVars = iDst;
+ pIntEnv->papszEnv[iDst] = NULL;
+
+ /* done */
+ *phEnv = pIntEnv;
+ }
+
+ RtlReleasePebLock();
+ return rc;
+
+#else /* !RT_OS_WINDOWS */
+
+ /*
+ * Figure out how many variable to clone.
+ */
+ const char * const *papszEnv = rtEnvDefault();
+ size_t cVars = 0;
+ if (papszEnv)
+ while (papszEnv[cVars])
+ cVars++;
+
+ bool fCaseSensitive = true;
+# 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
+
+ /*
+ * Create the duplicate.
+ */
+ PRTENVINTERNAL pIntEnv;
+ int rc = rtEnvCreate(&pIntEnv, cVars + 1 /* NULL */, fCaseSensitive, false /*fPutEnvBlock*/, fFirstEqual);
+ if (RT_SUCCESS(rc))
+ {
+ pIntEnv->cVars = cVars;
+ pIntEnv->papszEnv[pIntEnv->cVars] = NULL;
+
+ /* ASSUMES the default environment is in the current codepage. */
+ size_t iDst = 0;
+ for (size_t iSrc = 0; iSrc < cVars; iSrc++)
+ {
+ int rc2 = RTStrCurrentCPToUtf8(&pIntEnv->papszEnv[iDst], papszEnv[iSrc]);
+ 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;
+
+ /* done */
+ *phEnv = pIntEnv;
+ }
+
+ return rc;
+#endif /* !RT_OS_WINDOWS */
+}
+
+
+/**
+ * Clones a non-default environment instance.
+ *
+ * @param phEnv Where to return the handle to the cloned environment.
+ * @param pIntEnvToClone The source environment. Caller takes care of
+ * locking.
+ */
+static int rtEnvCloneNonDefault(PRTENV phEnv, PRTENVINTERNAL pIntEnvToClone)
+{
+ PRTENVINTERNAL pIntEnv;
+ size_t const cVars = pIntEnvToClone->cVars;
+ int rc = rtEnvCreate(&pIntEnv, cVars + 1 /* NULL */,
+ pIntEnvToClone->pfnCompare != RTStrNICmp,
+ pIntEnvToClone->fPutEnvBlock,
+ pIntEnvToClone->fFirstEqual);
+ if (RT_SUCCESS(rc))
+ {
+ pIntEnv->cVars = cVars;
+ pIntEnv->papszEnv[cVars] = NULL;
+
+ const char * const * const papszEnv = pIntEnvToClone->papszEnv;
+ for (size_t iVar = 0; iVar < cVars; iVar++)
+ {
+ char *pszVar = RTStrDup(papszEnv[iVar]);
+ if (RT_UNLIKELY(!pszVar))
+ {
+ pIntEnv->cVars = iVar;
+ RTEnvDestroy(pIntEnv);
+ return VERR_NO_STR_MEMORY;
+ }
+ pIntEnv->papszEnv[iVar] = pszVar;
+ }
+
+ /* done */
+ *phEnv = pIntEnv;
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTEnvClone(PRTENV phEnv, RTENV hEnvToClone)
+{
+ /*
+ * Validate input and what kind of source block we're working with.
+ */
+ int rc;
+ AssertPtrReturn(phEnv, VERR_INVALID_POINTER);
+ if (hEnvToClone == RTENV_DEFAULT)
+ rc = rtEnvCloneDefault(phEnv);
+ else
+ {
+ PRTENVINTERNAL pIntEnvToClone = hEnvToClone;
+ AssertPtrReturn(pIntEnvToClone, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnvToClone->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+
+ RTENV_LOCK(pIntEnvToClone);
+ rc = rtEnvCloneNonDefault(phEnv, pIntEnvToClone);
+ RTENV_UNLOCK(pIntEnvToClone);
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTEnvClone);
+
+
+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;
+#ifdef RTENV_ALLOW_EQUAL_FIRST_IN_VAR
+ int rc = rtEnvCreate(&pIntEnv, cVars + 1 /* NULL */, false /*fCaseSensitive*/, false /*fPutEnvBlock*/, true /*fFirstEqual*/);
+#else
+ int rc = rtEnvCreate(&pIntEnv, cVars + 1 /* NULL */, false /*fCaseSensitive*/, false /*fPutEnvBlock*/, false /*fFirstEqual*/);
+#endif
+ 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;
+}
+RT_EXPORT_SYMBOL(RTEnvCloneUtf16Block);
+
+
+
+RTDECL(int) RTEnvReset(RTENV hEnv)
+{
+ PRTENVINTERNAL pIntEnv = hEnv;
+ AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+
+ RTENV_LOCK(pIntEnv);
+
+ size_t iVar = pIntEnv->cVars;
+ pIntEnv->cVars = 0;
+ while (iVar-- > 0)
+ {
+ RTMemFree(pIntEnv->papszEnv[iVar]);
+ pIntEnv->papszEnv[iVar] = NULL;
+ }
+
+ RTENV_UNLOCK(pIntEnv);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTEnvReset);
+
+
+/**
+ * Appends an already allocated string to papszEnv.
+ *
+ * @returns IPRT status code
+ * @param pIntEnv The environment block to append it to.
+ * @param pszEntry The string to add. Already duplicated, caller
+ * does error cleanup.
+ */
+static int rtEnvIntAppend(PRTENVINTERNAL pIntEnv, char *pszEntry)
+{
+ /*
+ * Do we need to resize the array?
+ */
+ int rc = VINF_SUCCESS;
+ size_t iVar = pIntEnv->cVars;
+ if (iVar + 2 > pIntEnv->cAllocated)
+ {
+ void *pvNew = RTMemRealloc(pIntEnv->papszEnv, sizeof(char *) * (pIntEnv->cAllocated + RTENV_GROW_SIZE));
+ if (!pvNew)
+ rc = VERR_NO_MEMORY;
+ else
+ {
+ pIntEnv->papszEnv = (char **)pvNew;
+ pIntEnv->cAllocated += RTENV_GROW_SIZE;
+ for (size_t iNewVar = pIntEnv->cVars; iNewVar < pIntEnv->cAllocated; iNewVar++)
+ pIntEnv->papszEnv[iNewVar] = NULL;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Append it.
+ */
+ pIntEnv->papszEnv[iVar] = pszEntry;
+ pIntEnv->papszEnv[iVar + 1] = NULL; /* this isn't really necessary, but doesn't hurt. */
+ pIntEnv->cVars = iVar + 1;
+ }
+ return rc;
+}
+
+
+/**
+ * Worker for RTEnvSetEx and RTEnvPutEx.
+ */
+static int rtEnvSetExWorker(RTENV Env, const char *pchVar, size_t cchVar, const char *pszValue)
+{
+ int rc;
+ if (Env == RTENV_DEFAULT)
+ {
+#ifdef RT_OS_WINDOWS
+ extern int rtEnvSetUtf8Worker(const char *pchVar, size_t cchVar, const char *pszValue);
+ rc = rtEnvSetUtf8Worker(pchVar, cchVar, pszValue);
+#else
+ /*
+ * Since RTEnvPut isn't UTF-8 clean and actually expects the strings
+ * to be in the current code page (codeset), we'll do the necessary
+ * conversions here.
+ */
+ char *pszVarOtherCP;
+ rc = RTStrUtf8ToCurrentCPEx(&pszVarOtherCP, pchVar, cchVar);
+ if (RT_SUCCESS(rc))
+ {
+ char *pszValueOtherCP;
+ rc = RTStrUtf8ToCurrentCP(&pszValueOtherCP, pszValue);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTEnvSet(pszVarOtherCP, pszValueOtherCP);
+ RTStrFree(pszValueOtherCP);
+ }
+ RTStrFree(pszVarOtherCP);
+ }
+#endif
+ }
+ else
+ {
+ PRTENVINTERNAL pIntEnv = Env;
+ AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Create the variable string.
+ */
+ const size_t cchValue = strlen(pszValue);
+ char *pszEntry = (char *)RTMemAlloc(cchVar + cchValue + 2);
+ if (pszEntry)
+ {
+ memcpy(pszEntry, pchVar, cchVar);
+ pszEntry[cchVar] = '=';
+ memcpy(&pszEntry[cchVar + 1], pszValue, cchValue + 1);
+
+ RTENV_LOCK(pIntEnv);
+
+ /*
+ * Find the location of the variable. (iVar = cVars if new)
+ */
+ rc = VINF_SUCCESS;
+ size_t iVar;
+ for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
+ if ( !pIntEnv->pfnCompare(pIntEnv->papszEnv[iVar], pchVar, cchVar)
+ && ( pIntEnv->papszEnv[iVar][cchVar] == '='
+ || pIntEnv->papszEnv[iVar][cchVar] == '\0') )
+ break;
+ if (iVar < pIntEnv->cVars)
+ {
+ /*
+ * Replace the current entry. Simple.
+ */
+ RTMemFree(pIntEnv->papszEnv[iVar]);
+ pIntEnv->papszEnv[iVar] = pszEntry;
+ }
+ else
+ {
+ /*
+ * New variable, append it.
+ */
+ Assert(pIntEnv->cVars == iVar);
+ rc = rtEnvIntAppend(pIntEnv, pszEntry);
+ }
+
+ RTENV_UNLOCK(pIntEnv);
+
+ if (RT_FAILURE(rc))
+ RTMemFree(pszEntry);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTEnvSetEx(RTENV Env, const char *pszVar, const char *pszValue)
+{
+ AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
+ size_t const cchVar = strlen(pszVar);
+ AssertReturn(cchVar > 0, VERR_ENV_INVALID_VAR_NAME);
+ char const *pszEq = (char const *)memchr(pszVar, '=', cchVar);
+ if (!pszEq)
+ { /* likely */ }
+ else
+ {
+ AssertReturn(Env != RTENV_DEFAULT, VERR_ENV_INVALID_VAR_NAME);
+ PRTENVINTERNAL pIntEnv = Env;
+ AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->fFirstEqual, VERR_ENV_INVALID_VAR_NAME);
+ AssertReturn(memchr(pszVar + 1, '=', cchVar - 1) == NULL, VERR_ENV_INVALID_VAR_NAME);
+ }
+
+ return rtEnvSetExWorker(Env, pszVar, cchVar, pszValue);
+}
+RT_EXPORT_SYMBOL(RTEnvSetEx);
+
+
+RTDECL(int) RTEnvUnsetEx(RTENV Env, const char *pszVar)
+{
+ AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
+ AssertReturn(*pszVar, VERR_ENV_INVALID_VAR_NAME);
+
+ int rc;
+ if (Env == RTENV_DEFAULT)
+ {
+#ifdef RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API
+ rc = RTEnvUnsetUtf8(pszVar);
+#else
+ /*
+ * Since RTEnvUnset isn't UTF-8 clean and actually expects the strings
+ * to be in the current code page (codeset), we'll do the necessary
+ * conversions here.
+ */
+ char *pszVarOtherCP;
+ rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTEnvUnset(pszVarOtherCP);
+ RTStrFree(pszVarOtherCP);
+ }
+#endif
+ }
+ else
+ {
+ PRTENVINTERNAL pIntEnv = Env;
+ AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+ const size_t cchVar = strlen(pszVar);
+ AssertReturn(cchVar > 0, VERR_ENV_INVALID_VAR_NAME);
+ AssertReturn(strchr(pIntEnv->fFirstEqual ? pszVar + 1 : pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
+
+ RTENV_LOCK(pIntEnv);
+
+ /*
+ * Remove all variable by the given name.
+ */
+ rc = VINF_ENV_VAR_NOT_FOUND;
+ size_t iVar;
+ for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
+ if ( !pIntEnv->pfnCompare(pIntEnv->papszEnv[iVar], pszVar, cchVar)
+ && ( pIntEnv->papszEnv[iVar][cchVar] == '='
+ || pIntEnv->papszEnv[iVar][cchVar] == '\0') )
+ {
+ if (!pIntEnv->fPutEnvBlock)
+ {
+ RTMemFree(pIntEnv->papszEnv[iVar]);
+ pIntEnv->cVars--;
+ if (pIntEnv->cVars > 0)
+ pIntEnv->papszEnv[iVar] = pIntEnv->papszEnv[pIntEnv->cVars];
+ pIntEnv->papszEnv[pIntEnv->cVars] = NULL;
+ }
+ else
+ {
+ /* Record this unset by keeping the variable without any equal sign. */
+ pIntEnv->papszEnv[iVar][cchVar] = '\0';
+ }
+ rc = VINF_SUCCESS;
+ /* no break, there could be more. */
+ }
+
+ /*
+ * If this is a change record, we may need to add it.
+ */
+ if (rc == VINF_ENV_VAR_NOT_FOUND && pIntEnv->fPutEnvBlock)
+ {
+ char *pszEntry = (char *)RTMemDup(pszVar, cchVar + 1);
+ if (pszEntry)
+ {
+ rc = rtEnvIntAppend(pIntEnv, pszEntry);
+ if (RT_SUCCESS(rc))
+ rc = VINF_ENV_VAR_NOT_FOUND;
+ else
+ RTMemFree(pszEntry);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ RTENV_UNLOCK(pIntEnv);
+ }
+ return rc;
+
+}
+RT_EXPORT_SYMBOL(RTEnvUnsetEx);
+
+
+RTDECL(int) RTEnvPutEx(RTENV Env, const char *pszVarEqualValue)
+{
+ int rc;
+ AssertPtrReturn(pszVarEqualValue, VERR_INVALID_POINTER);
+ const char *pszEq = strchr(pszVarEqualValue, '=');
+ if ( pszEq == pszVarEqualValue
+ && Env != RTENV_DEFAULT)
+ {
+ PRTENVINTERNAL pIntEnv = Env;
+ AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+ if (pIntEnv->fFirstEqual)
+ pszEq = strchr(pszVarEqualValue + 1, '=');
+ }
+ if (!pszEq)
+ rc = RTEnvUnsetEx(Env, pszVarEqualValue);
+ else
+ {
+ AssertReturn(pszEq != pszVarEqualValue, VERR_ENV_INVALID_VAR_NAME);
+ rc = rtEnvSetExWorker(Env, pszVarEqualValue, pszEq - pszVarEqualValue, pszEq + 1);
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTEnvPutEx);
+
+
+RTDECL(int) RTEnvGetEx(RTENV Env, const char *pszVar, char *pszValue, size_t cbValue, size_t *pcchActual)
+{
+ AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pszValue, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pcchActual, VERR_INVALID_POINTER);
+ AssertReturn(pcchActual || (pszValue && cbValue), VERR_INVALID_PARAMETER);
+
+ if (pcchActual)
+ *pcchActual = 0;
+ int rc;
+ if (Env == RTENV_DEFAULT)
+ {
+#ifdef RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API
+ rc = RTEnvGetUtf8(pszVar, pszValue, cbValue, pcchActual);
+#else
+ /*
+ * Since RTEnvGet isn't UTF-8 clean and actually expects the strings
+ * to be in the current code page (codeset), we'll do the necessary
+ * conversions here.
+ */
+ char *pszVarOtherCP;
+ rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszValueOtherCP = RTEnvGet(pszVarOtherCP);
+ RTStrFree(pszVarOtherCP);
+ if (pszValueOtherCP)
+ {
+ char *pszValueUtf8;
+ rc = RTStrCurrentCPToUtf8(&pszValueUtf8, pszValueOtherCP);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VINF_SUCCESS;
+ size_t cch = strlen(pszValueUtf8);
+ if (pcchActual)
+ *pcchActual = cch;
+ if (pszValue && cbValue)
+ {
+ if (cch < cbValue)
+ memcpy(pszValue, pszValueUtf8, cch + 1);
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ RTStrFree(pszValueUtf8);
+ }
+ }
+ else
+ rc = VERR_ENV_VAR_NOT_FOUND;
+ }
+#endif
+ }
+ else
+ {
+ PRTENVINTERNAL pIntEnv = Env;
+ AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+ const size_t cchVar = strlen(pszVar);
+ AssertReturn(cchVar > 0, VERR_ENV_INVALID_VAR_NAME);
+ AssertReturn(strchr(pIntEnv->fFirstEqual ? pszVar + 1 : pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
+
+ RTENV_LOCK(pIntEnv);
+
+ /*
+ * Locate the first variable and return it to the caller.
+ */
+ rc = VERR_ENV_VAR_NOT_FOUND;
+ size_t iVar;
+ for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
+ if (!pIntEnv->pfnCompare(pIntEnv->papszEnv[iVar], pszVar, cchVar))
+ {
+ if (pIntEnv->papszEnv[iVar][cchVar] == '=')
+ {
+ rc = VINF_SUCCESS;
+ const char *pszValueOrg = pIntEnv->papszEnv[iVar] + cchVar + 1;
+ size_t cch = strlen(pszValueOrg);
+ if (pcchActual)
+ *pcchActual = cch;
+ if (pszValue && cbValue)
+ {
+ if (cch < cbValue)
+ memcpy(pszValue, pszValueOrg, cch + 1);
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ break;
+ }
+ if (pIntEnv->papszEnv[iVar][cchVar] == '\0')
+ {
+ Assert(pIntEnv->fPutEnvBlock);
+ rc = VERR_ENV_VAR_UNSET;
+ break;
+ }
+ }
+
+ RTENV_UNLOCK(pIntEnv);
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTEnvGetEx);
+
+
+RTDECL(bool) RTEnvExistEx(RTENV Env, const char *pszVar)
+{
+ AssertPtrReturn(pszVar, false);
+
+ bool fExists = false;
+ if (Env == RTENV_DEFAULT)
+ {
+#ifdef RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API
+ fExists = RTEnvExistsUtf8(pszVar);
+#else
+ /*
+ * Since RTEnvExist isn't UTF-8 clean and actually expects the strings
+ * to be in the current code page (codeset), we'll do the necessary
+ * conversions here.
+ */
+ char *pszVarOtherCP;
+ int rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
+ if (RT_SUCCESS(rc))
+ {
+ fExists = RTEnvExist(pszVarOtherCP);
+ RTStrFree(pszVarOtherCP);
+ }
+#endif
+ }
+ else
+ {
+ PRTENVINTERNAL pIntEnv = Env;
+ AssertPtrReturn(pIntEnv, false);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, false);
+ const size_t cchVar = strlen(pszVar);
+ AssertReturn(cchVar > 0, false);
+ AssertReturn(strchr(pIntEnv->fFirstEqual ? pszVar + 1 : pszVar, '=') == NULL, false);
+
+ RTENV_LOCK(pIntEnv);
+
+ /*
+ * Simple search.
+ */
+ for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
+ if (!pIntEnv->pfnCompare(pIntEnv->papszEnv[iVar], pszVar, cchVar))
+ {
+ if (pIntEnv->papszEnv[iVar][cchVar] == '=')
+ {
+ fExists = true;
+ break;
+ }
+ if (pIntEnv->papszEnv[iVar][cchVar] == '\0')
+ break;
+ }
+
+ RTENV_UNLOCK(pIntEnv);
+ }
+ return fExists;
+}
+RT_EXPORT_SYMBOL(RTEnvExistEx);
+
+
+#ifndef RT_OS_WINDOWS
+RTDECL(char const * const *) RTEnvGetExecEnvP(RTENV Env)
+{
+ const char * const *papszRet;
+ if (Env == RTENV_DEFAULT)
+ {
+ /** @todo fix this API it's fundamentally wrong! */
+ papszRet = rtEnvDefault();
+ if (!papszRet)
+ {
+ static const char * const s_papszDummy[2] = { NULL, NULL };
+ papszRet = &s_papszDummy[0];
+ }
+ }
+ else
+ {
+ PRTENVINTERNAL pIntEnv = Env;
+ AssertPtrReturn(pIntEnv, NULL);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, NULL);
+
+ RTENV_LOCK(pIntEnv);
+
+ /*
+ * Free any old envp.
+ */
+ if (pIntEnv->papszEnvOtherCP)
+ {
+ for (size_t iVar = 0; pIntEnv->papszEnvOtherCP[iVar]; iVar++)
+ {
+ RTStrFree(pIntEnv->papszEnvOtherCP[iVar]);
+ pIntEnv->papszEnvOtherCP[iVar] = NULL;
+ }
+ RTMemFree(pIntEnv->papszEnvOtherCP);
+ pIntEnv->papszEnvOtherCP = NULL;
+ }
+
+ /*
+ * Construct a new envp with the strings in the process code set.
+ */
+ char **papsz;
+ papszRet = pIntEnv->papszEnvOtherCP = papsz = (char **)RTMemAlloc(sizeof(char *) * (pIntEnv->cVars + 1));
+ if (papsz)
+ {
+ papsz[pIntEnv->cVars] = NULL;
+ for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
+ {
+ int rc = RTStrUtf8ToCurrentCP(&papsz[iVar], pIntEnv->papszEnv[iVar]);
+ if (RT_FAILURE(rc))
+ {
+ /* RTEnvDestroy / we cleans up later. */
+ papsz[iVar] = NULL;
+ AssertRC(rc);
+ papszRet = NULL;
+ break;
+ }
+ }
+ }
+
+ RTENV_UNLOCK(pIntEnv);
+ }
+ return papszRet;
+}
+RT_EXPORT_SYMBOL(RTEnvGetExecEnvP);
+#endif /* !RT_OS_WINDOWS */
+
+
+/**
+ * RTSort callback for comparing two environment variables.
+ *
+ * @returns -1, 0, 1. See PFNRTSORTCMP.
+ * @param pvElement1 Variable 1.
+ * @param pvElement2 Variable 2.
+ * @param pvUser Ignored.
+ */
+static DECLCALLBACK(int) rtEnvSortCompare(const void *pvElement1, const void *pvElement2, void *pvUser)
+{
+ NOREF(pvUser);
+ int iDiff = strcmp((const char *)pvElement1, (const char *)pvElement2);
+ if (iDiff < 0)
+ iDiff = -1;
+ else if (iDiff > 0)
+ iDiff = 1;
+ return iDiff;
+}
+
+
+RTDECL(int) RTEnvQueryUtf16Block(RTENV hEnv, PRTUTF16 *ppwszzBlock)
+{
+ RTENV hClone = NIL_RTENV;
+ PRTENVINTERNAL pIntEnv;
+ int rc;
+
+ /*
+ * Validate / simplify input.
+ */
+ if (hEnv == RTENV_DEFAULT)
+ {
+ rc = RTEnvClone(&hClone, RTENV_DEFAULT);
+ if (RT_FAILURE(rc))
+ return rc;
+ pIntEnv = hClone;
+ }
+ else
+ {
+ pIntEnv = hEnv;
+ AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+ rc = VINF_SUCCESS;
+ }
+
+ RTENV_LOCK(pIntEnv);
+
+ /*
+ * Sort it first.
+ */
+ RTSortApvShell((void **)pIntEnv->papszEnv, pIntEnv->cVars, rtEnvSortCompare, pIntEnv);
+
+ /*
+ * Calculate the size.
+ */
+ size_t cwc;
+ size_t cwcTotal = 2;
+ for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
+ {
+ rc = RTStrCalcUtf16LenEx(pIntEnv->papszEnv[iVar], RTSTR_MAX, &cwc);
+ AssertRCBreak(rc);
+ cwcTotal += cwc + 1;
+ }
+
+ PRTUTF16 pwszzBlock = NULL;
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Perform the conversion.
+ */
+ PRTUTF16 pwszz = pwszzBlock = (PRTUTF16)RTMemAlloc(cwcTotal * sizeof(RTUTF16));
+ if (pwszz)
+ {
+ size_t cwcLeft = cwcTotal;
+ for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
+ {
+ rc = RTStrToUtf16Ex(pIntEnv->papszEnv[iVar], RTSTR_MAX,
+ &pwszz, cwcTotal - (pwszz - pwszzBlock), &cwc);
+ AssertRCBreak(rc);
+ pwszz += cwc + 1;
+ cwcLeft -= cwc + 1;
+ AssertBreakStmt(cwcLeft >= 2, rc = VERR_INTERNAL_ERROR_3);
+ }
+ AssertStmt(cwcLeft == 2 || RT_FAILURE(rc), rc = VERR_INTERNAL_ERROR_2);
+ if (RT_SUCCESS(rc))
+ {
+ pwszz[0] = '\0';
+ pwszz[1] = '\0';
+ }
+ else
+ {
+ RTMemFree(pwszzBlock);
+ pwszzBlock = NULL;
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ RTENV_UNLOCK(pIntEnv);
+
+ if (hClone != NIL_RTENV)
+ RTEnvDestroy(hClone);
+ if (RT_SUCCESS(rc))
+ *ppwszzBlock = pwszzBlock;
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTEnvQueryUtf16Block);
+
+
+RTDECL(void) RTEnvFreeUtf16Block(PRTUTF16 pwszzBlock)
+{
+ RTMemFree(pwszzBlock);
+}
+RT_EXPORT_SYMBOL(RTEnvFreeUtf16Block);
+
+
+RTDECL(int) RTEnvQueryUtf8Block(RTENV hEnv, bool fSorted, char **ppszzBlock, size_t *pcbBlock)
+{
+ RTENV hClone = NIL_RTENV;
+ PRTENVINTERNAL pIntEnv;
+ int rc;
+
+ /*
+ * Validate / simplify input.
+ */
+ if (hEnv == RTENV_DEFAULT)
+ {
+ rc = RTEnvClone(&hClone, RTENV_DEFAULT);
+ if (RT_FAILURE(rc))
+ return rc;
+ pIntEnv = hClone;
+ }
+ else
+ {
+ pIntEnv = hEnv;
+ AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+ rc = VINF_SUCCESS;
+ }
+
+ RTENV_LOCK(pIntEnv);
+
+ /*
+ * Sort it, if requested.
+ */
+ if (fSorted)
+ RTSortApvShell((void **)pIntEnv->papszEnv, pIntEnv->cVars, rtEnvSortCompare, pIntEnv);
+
+ /*
+ * Calculate the size. We add one extra terminator just to be on the safe side.
+ */
+ size_t cbBlock = 2;
+ for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
+ cbBlock += strlen(pIntEnv->papszEnv[iVar]) + 1;
+
+ if (pcbBlock)
+ *pcbBlock = cbBlock - 1;
+
+ /*
+ * Allocate memory and copy out the variables.
+ */
+ char *pszzBlock;
+ char *pszz = pszzBlock = (char *)RTMemAlloc(cbBlock);
+ if (pszz)
+ {
+ size_t cbLeft = cbBlock;
+ for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
+ {
+ size_t cb = strlen(pIntEnv->papszEnv[iVar]) + 1;
+ AssertBreakStmt(cb + 2 <= cbLeft, rc = VERR_INTERNAL_ERROR_3);
+ memcpy(pszz, pIntEnv->papszEnv[iVar], cb);
+ pszz += cb;
+ cbLeft -= cb;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ pszz[0] = '\0';
+ pszz[1] = '\0'; /* The extra one. */
+ }
+ else
+ {
+ RTMemFree(pszzBlock);
+ pszzBlock = NULL;
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ RTENV_UNLOCK(pIntEnv);
+
+ if (hClone != NIL_RTENV)
+ RTEnvDestroy(hClone);
+ if (RT_SUCCESS(rc))
+ *ppszzBlock = pszzBlock;
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTEnvQueryUtf8Block);
+
+
+RTDECL(void) RTEnvFreeUtf8Block(char *pszzBlock)
+{
+ RTMemFree(pszzBlock);
+}
+RT_EXPORT_SYMBOL(RTEnvFreeUtf8Block);
+
+
+RTDECL(uint32_t) RTEnvCountEx(RTENV hEnv)
+{
+ PRTENVINTERNAL pIntEnv = hEnv;
+ AssertPtrReturn(pIntEnv, UINT32_MAX);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, UINT32_MAX);
+
+ RTENV_LOCK(pIntEnv);
+ uint32_t cVars = (uint32_t)pIntEnv->cVars;
+ RTENV_UNLOCK(pIntEnv);
+
+ return cVars;
+}
+RT_EXPORT_SYMBOL(RTEnvCountEx);
+
+
+RTDECL(int) RTEnvGetByIndexEx(RTENV hEnv, uint32_t iVar, char *pszVar, size_t cbVar, char *pszValue, size_t cbValue)
+{
+ PRTENVINTERNAL pIntEnv = hEnv;
+ AssertPtrReturn(pIntEnv, UINT32_MAX);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, UINT32_MAX);
+ if (cbVar)
+ AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
+ if (cbValue)
+ AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
+
+ RTENV_LOCK(pIntEnv);
+
+ int rc;
+ if (iVar < pIntEnv->cVars)
+ {
+ const char *pszSrcVar = pIntEnv->papszEnv[iVar];
+ const char *pszSrcValue = strchr(pszSrcVar, '=');
+ if (pszSrcValue == pszSrcVar && pIntEnv->fFirstEqual)
+ pszSrcValue = strchr(pszSrcVar + 1, '=');
+ bool fHasEqual = pszSrcValue != NULL;
+ if (pszSrcValue)
+ {
+ pszSrcValue++;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ pszSrcValue = strchr(pszSrcVar, '\0');
+ rc = VINF_ENV_VAR_UNSET;
+ }
+ if (cbVar)
+ {
+ int rc2 = RTStrCopyEx(pszVar, cbVar, pszSrcVar, pszSrcValue - pszSrcVar - fHasEqual);
+ if (RT_FAILURE(rc2))
+ rc = rc2;
+ }
+ if (cbValue)
+ {
+ int rc2 = RTStrCopy(pszValue, cbValue, pszSrcValue);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ }
+ else
+ rc = VERR_ENV_VAR_NOT_FOUND;
+
+ RTENV_UNLOCK(pIntEnv);
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTEnvGetByIndexEx);
+
+
+RTDECL(const char *) RTEnvGetByIndexRawEx(RTENV hEnv, uint32_t iVar)
+{
+ PRTENVINTERNAL pIntEnv = hEnv;
+ AssertPtrReturn(pIntEnv, NULL);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, NULL);
+
+ RTENV_LOCK(pIntEnv);
+
+ const char *pszRet;
+ if (iVar < pIntEnv->cVars)
+ pszRet = pIntEnv->papszEnv[iVar];
+ else
+ pszRet = NULL;
+
+ RTENV_UNLOCK(pIntEnv);
+
+ return pszRet;
+}
+RT_EXPORT_SYMBOL(RTEnvGetByIndexRawEx);
+
+
+RTDECL(int) RTEnvCreateChangeRecord(PRTENV phEnv)
+{
+ AssertPtrReturn(phEnv, VERR_INVALID_POINTER);
+#ifdef RTENV_ALLOW_EQUAL_FIRST_IN_VAR
+ return rtEnvCreate(phEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, true /*fPutEnvBlock*/, true /*fFirstEqual*/);
+#else
+ return rtEnvCreate(phEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, true /*fPutEnvBlock*/, false /*fFirstEqual*/);
+#endif
+}
+RT_EXPORT_SYMBOL(RTEnvCreateChangeRecord);
+
+
+RTDECL(int) RTEnvCreateChangeRecordEx(PRTENV phEnv, uint32_t fFlags)
+{
+ AssertPtrReturn(phEnv, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTENV_CREATE_F_VALID_MASK), VERR_INVALID_FLAGS);
+ return rtEnvCreate(phEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, true /*fPutEnvBlock*/,
+ RT_BOOL(fFlags & RTENV_CREATE_F_ALLOW_EQUAL_FIRST_IN_VAR));
+}
+RT_EXPORT_SYMBOL(RTEnvCreateChangeRecord);
+
+
+RTDECL(bool) RTEnvIsChangeRecord(RTENV hEnv)
+{
+ if (hEnv == RTENV_DEFAULT)
+ return false;
+
+ PRTENVINTERNAL pIntEnv = hEnv;
+ AssertPtrReturn(pIntEnv, false);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, false);
+ return pIntEnv->fPutEnvBlock;
+}
+RT_EXPORT_SYMBOL(RTEnvIsChangeRecord);
+
+
+RTDECL(int) RTEnvApplyChanges(RTENV hEnvDst, RTENV hEnvChanges)
+{
+ PRTENVINTERNAL pIntEnvChanges = hEnvChanges;
+ AssertPtrReturn(pIntEnvChanges, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnvChanges->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+
+ /** @todo lock validator trouble ahead here! */
+ RTENV_LOCK(pIntEnvChanges);
+
+ int rc = VINF_SUCCESS;
+ for (uint32_t iChange = 0; iChange < pIntEnvChanges->cVars && RT_SUCCESS(rc); iChange++)
+ rc = RTEnvPutEx(hEnvDst, pIntEnvChanges->papszEnv[iChange]);
+
+ RTENV_UNLOCK(pIntEnvChanges);
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTEnvApplyChanges);
+