VirtualBox

source: vbox/trunk/src/VBox/Runtime/generic/env-generic.cpp@ 80827

Last change on this file since 80827 was 80827, checked in by vboxsync, 6 years ago

IPRT: Added RTEnvCreateEx and RTEnvCreateChangeRecordEx so flags can be specified when creating custom environments. Defined one flag RTENV_CREATE_F_ALLOW_EQUAL_FIRST_IN_VAR for accomodating windows style environment variables (used for CWD by driver letter). The flag is set by default on windows hosts, however it does not work for the default environment due to CRT limitations.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 40.6 KB
Line 
1/* $Id: env-generic.cpp 80827 2019-09-16 14:04:02Z vboxsync $ */
2/** @file
3 * IPRT - Environment, Generic.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/env.h>
32#include "internal/iprt.h"
33
34#include <iprt/assert.h>
35#include <iprt/alloc.h>
36#include <iprt/alloca.h>
37#include <iprt/err.h>
38#include <iprt/sort.h>
39#include <iprt/string.h>
40#include <iprt/utf16.h>
41#include "internal/magics.h"
42
43#include <stdlib.h>
44#if !defined(RT_OS_WINDOWS)
45# include <unistd.h>
46#endif
47#ifdef RT_OS_DARWIN
48# include <crt_externs.h>
49#endif
50#if defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) || defined(RT_OS_NETBSD) || defined(RT_OS_OPENBSD)
51RT_C_DECLS_BEGIN
52extern char **environ;
53RT_C_DECLS_END
54#endif
55
56
57/*********************************************************************************************************************************
58* Defined Constants And Macros *
59*********************************************************************************************************************************/
60/** The allocation granularity of the RTENVINTERNAL::papszEnv memory. */
61#define RTENV_GROW_SIZE 16
62
63/** Macro that unlocks the specified environment block. */
64#define RTENV_LOCK(pEnvInt) do { } while (0)
65/** Macro that unlocks the specified environment block. */
66#define RTENV_UNLOCK(pEnvInt) do { } while (0)
67
68/** @def RTENV_HAVE_WENVIRON
69 * Indicates that we have a _wenviron variable with UTF-16 strings that we
70 * better use instead of the current-cp strings in environ. */
71#if defined(RT_OS_WINDOWS) || defined(DOXYGEN_RUNNING)
72# define RTENV_HAVE_WENVIRON 1
73#endif
74
75/** @def RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API
76 * Indicates the RTEnv*Utf8 APIs are implemented. */
77#if defined(RT_OS_WINDOWS) || defined(DOXYGEN_RUNNING)
78# define RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API 1
79#endif
80
81
82/** @def RTENV_ALLOW_EQUAL_FIRST_IN_VAR
83 * Allows a variable to start with an '=' sign. This is used by windows to
84 * maintain CWDs of non-current drives.
85 * @note Not supported by _wputenv AFAIK. */
86#if defined(RT_OS_WINDOWS) || defined(DOXYGEN_RUNNING)
87# define RTENV_ALLOW_EQUAL_FIRST_IN_VAR 1
88#endif
89
90
91/*********************************************************************************************************************************
92* Structures and Typedefs *
93*********************************************************************************************************************************/
94/**
95 * The internal representation of a (non-default) environment.
96 */
97typedef struct RTENVINTERNAL
98{
99 /** Magic value . */
100 uint32_t u32Magic;
101 /** Set if this is a record of environment changes, putenv style. */
102 bool fPutEnvBlock;
103 /** Set if starting a variable with an equal sign is okay, clear if not okay
104 * (RTENV_CREATE_F_ALLOW_EQUAL_FIRST_IN_VAR). */
105 bool fFirstEqual;
106 /** Number of variables in the array.
107 * This does not include the terminating NULL entry. */
108 size_t cVars;
109 /** Capacity (allocated size) of the array.
110 * This includes space for the terminating NULL element (for compatibility
111 * with the C library), so that c <= cCapacity - 1. */
112 size_t cAllocated;
113 /** Array of environment variables.
114 * These are always in "NAME=VALUE" form, where the value can be empty. If
115 * fPutEnvBlock is set though, there will be "NAME" entries too for variables
116 * that need to be removed when merged with another environment block. */
117 char **papszEnv;
118 /** Array of environment variables in the process CP.
119 * This get (re-)constructed when RTEnvGetExecEnvP method is called. */
120 char **papszEnvOtherCP;
121
122 /** The compare function we're using. */
123 DECLCALLBACKMEMBER(int, pfnCompare)(const char *psz1, const char *psz2, size_t cchMax);
124} RTENVINTERNAL, *PRTENVINTERNAL;
125
126
127/**
128 * Internal worker that resolves the pointer to the default
129 * process environment. (environ)
130 *
131 * @returns Pointer to the default environment.
132 * This may be NULL.
133 */
134static const char * const *rtEnvDefault(void)
135{
136#ifdef RT_OS_DARWIN
137 return *(_NSGetEnviron());
138#else
139 return environ;
140#endif
141}
142
143
144/**
145 * Internal worker that creates an environment handle with a specified capacity.
146 *
147 * @returns IPRT status code.
148 * @param ppIntEnv Where to store the result.
149 * @param cAllocated The initial array size.
150 * @param fCaseSensitive Whether the environment block is case sensitive or
151 * not.
152 * @param fPutEnvBlock Indicates whether this is a special environment
153 * block that will be used to record change another
154 * block. We will keep unsets in putenv format, i.e.
155 * just the variable name without any equal sign.
156 * @param fFirstEqual The RTENV_CREATE_F_ALLOW_EQUAL_FIRST_IN_VAR value.
157 */
158static int rtEnvCreate(PRTENVINTERNAL *ppIntEnv, size_t cAllocated, bool fCaseSensitive, bool fPutEnvBlock, bool fFirstEqual)
159{
160 /*
161 * Allocate environment handle.
162 */
163 PRTENVINTERNAL pIntEnv = (PRTENVINTERNAL)RTMemAlloc(sizeof(*pIntEnv));
164 if (pIntEnv)
165 {
166 /*
167 * Pre-allocate the variable array.
168 */
169 pIntEnv->u32Magic = RTENV_MAGIC;
170 pIntEnv->fPutEnvBlock = fPutEnvBlock;
171 pIntEnv->fFirstEqual = fFirstEqual;
172 pIntEnv->pfnCompare = fCaseSensitive ? RTStrNCmp : RTStrNICmp;
173 pIntEnv->papszEnvOtherCP = NULL;
174 pIntEnv->cVars = 0;
175 pIntEnv->cAllocated = RT_ALIGN_Z(RT_MAX(cAllocated, RTENV_GROW_SIZE), RTENV_GROW_SIZE);
176 pIntEnv->papszEnv = (char **)RTMemAllocZ(sizeof(pIntEnv->papszEnv[0]) * pIntEnv->cAllocated);
177 if (pIntEnv->papszEnv)
178 {
179 *ppIntEnv = pIntEnv;
180 return VINF_SUCCESS;
181 }
182
183 RTMemFree(pIntEnv);
184 }
185
186 return VERR_NO_MEMORY;
187}
188
189
190RTDECL(int) RTEnvCreate(PRTENV pEnv)
191{
192 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
193#ifdef RTENV_ALLOW_EQUAL_FIRST_IN_VAR
194 return rtEnvCreate(pEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, false /*fPutEnvBlock*/, true /*fFirstEqual*/);
195#else
196 return rtEnvCreate(pEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, false /*fPutEnvBlock*/, false/*fFirstEqual*/);
197#endif
198}
199RT_EXPORT_SYMBOL(RTEnvCreate);
200
201
202RTDECL(int) RTEnvCreateEx(PRTENV phEnv, uint32_t fFlags)
203{
204 AssertPtrReturn(phEnv, VERR_INVALID_POINTER);
205 AssertReturn(!(fFlags & ~RTENV_CREATE_F_VALID_MASK), VERR_INVALID_FLAGS);
206 return rtEnvCreate(phEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, false /*fPutEnvBlock*/,
207 RT_BOOL(fFlags & RTENV_CREATE_F_ALLOW_EQUAL_FIRST_IN_VAR));
208}
209RT_EXPORT_SYMBOL(RTEnvCreateEx);
210
211
212RTDECL(int) RTEnvDestroy(RTENV Env)
213{
214 /*
215 * Ignore NIL_RTENV and validate input.
216 */
217 if ( Env == NIL_RTENV
218 || Env == RTENV_DEFAULT)
219 return VINF_SUCCESS;
220
221 PRTENVINTERNAL pIntEnv = Env;
222 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
223 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
224
225 /*
226 * Do the cleanup.
227 */
228 RTENV_LOCK(pIntEnv);
229 pIntEnv->u32Magic++;
230 size_t iVar = pIntEnv->cVars;
231 while (iVar-- > 0)
232 RTStrFree(pIntEnv->papszEnv[iVar]);
233 RTMemFree(pIntEnv->papszEnv);
234 pIntEnv->papszEnv = NULL;
235
236 if (pIntEnv->papszEnvOtherCP)
237 {
238 for (iVar = 0; pIntEnv->papszEnvOtherCP[iVar]; iVar++)
239 {
240 RTStrFree(pIntEnv->papszEnvOtherCP[iVar]);
241 pIntEnv->papszEnvOtherCP[iVar] = NULL;
242 }
243 RTMemFree(pIntEnv->papszEnvOtherCP);
244 pIntEnv->papszEnvOtherCP = NULL;
245 }
246
247 RTENV_UNLOCK(pIntEnv);
248 /*RTCritSectDelete(&pIntEnv->CritSect) */
249 RTMemFree(pIntEnv);
250
251 return VINF_SUCCESS;
252}
253RT_EXPORT_SYMBOL(RTEnvDestroy);
254
255
256RTDECL(int) RTEnvClone(PRTENV pEnv, RTENV EnvToClone)
257{
258 /*
259 * Validate input and figure out how many variable to clone and where to get them.
260 */
261 bool fCaseSensitive = true;
262 bool fPutEnvBlock = false;
263 bool fFirstEqual = false;
264 size_t cVars;
265 const char * const *papszEnv;
266#ifdef RTENV_HAVE_WENVIRON
267 PCRTUTF16 const * papwszEnv = NULL;
268#endif
269 PRTENVINTERNAL pIntEnvToClone;
270 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
271 if (EnvToClone == RTENV_DEFAULT)
272 {
273 cVars = 0;
274 pIntEnvToClone = NULL;
275#ifdef RTENV_HAVE_WENVIRON
276 papszEnv = NULL;
277 papwszEnv = (PCRTUTF16 * const)_wenviron;
278 if (!papwszEnv)
279 {
280 _wgetenv(L"Path"); /* Force the CRT to initalize it. */
281 papwszEnv = (PCRTUTF16 * const)_wenviron;
282 }
283 if (papwszEnv)
284 while (papwszEnv[cVars])
285 cVars++;
286#else
287 papszEnv = rtEnvDefault();
288 if (papszEnv)
289 while (papszEnv[cVars])
290 cVars++;
291#endif
292
293#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
294 /* DOS systems was case insensitive. A prime example is the 'Path'
295 variable on windows which turns into the 'PATH' variable. */
296 fCaseSensitive = false;
297#endif
298#ifdef RTENV_ALLOW_EQUAL_FIRST_IN_VAR
299 fFirstEqual = true;
300#endif
301 }
302 else
303 {
304 pIntEnvToClone = EnvToClone;
305 AssertPtrReturn(pIntEnvToClone, VERR_INVALID_HANDLE);
306 AssertReturn(pIntEnvToClone->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
307 RTENV_LOCK(pIntEnvToClone);
308
309 fPutEnvBlock = pIntEnvToClone->fPutEnvBlock;
310 fFirstEqual = pIntEnvToClone->fFirstEqual;
311 papszEnv = pIntEnvToClone->papszEnv;
312 cVars = pIntEnvToClone->cVars;
313 }
314
315 /*
316 * Create the duplicate.
317 */
318 PRTENVINTERNAL pIntEnv;
319 int rc = rtEnvCreate(&pIntEnv, cVars + 1 /* NULL */, fCaseSensitive, fPutEnvBlock, fFirstEqual);
320 if (RT_SUCCESS(rc))
321 {
322 pIntEnv->cVars = cVars;
323 pIntEnv->papszEnv[pIntEnv->cVars] = NULL;
324 if (EnvToClone == RTENV_DEFAULT)
325 {
326 /* ASSUMES the default environment is in the current codepage. */
327 size_t iDst = 0;
328 for (size_t iSrc = 0; iSrc < cVars; iSrc++)
329 {
330#ifdef RTENV_HAVE_WENVIRON
331 int rc2 = RTUtf16ToUtf8(papwszEnv[iSrc], &pIntEnv->papszEnv[iDst]);
332#else
333 int rc2 = RTStrCurrentCPToUtf8(&pIntEnv->papszEnv[iDst], papszEnv[iSrc]);
334#endif
335 if (RT_SUCCESS(rc2))
336 {
337 /* Make sure it contains an '='. */
338 iDst++;
339 if (strchr(pIntEnv->papszEnv[iDst - 1], '='))
340 continue;
341 rc2 = RTStrAAppend(&pIntEnv->papszEnv[iDst - 1], "=");
342 if (RT_SUCCESS(rc2))
343 continue;
344 }
345 else if (rc2 == VERR_NO_TRANSLATION)
346 {
347 rc = VWRN_ENV_NOT_FULLY_TRANSLATED;
348 continue;
349 }
350
351 /* failed fatally. */
352 pIntEnv->cVars = iDst;
353 RTEnvDestroy(pIntEnv);
354 return rc2;
355 }
356 pIntEnv->cVars = iDst;
357 }
358 else
359 {
360 for (size_t iVar = 0; iVar < cVars; iVar++)
361 {
362 char *pszVar = RTStrDup(papszEnv[iVar]);
363 if (RT_UNLIKELY(!pszVar))
364 {
365 RTENV_UNLOCK(pIntEnvToClone);
366
367 pIntEnv->cVars = iVar;
368 RTEnvDestroy(pIntEnv);
369 return VERR_NO_STR_MEMORY;
370 }
371 pIntEnv->papszEnv[iVar] = pszVar;
372 }
373 }
374
375 /* done */
376 *pEnv = pIntEnv;
377 }
378
379 if (pIntEnvToClone)
380 RTENV_UNLOCK(pIntEnvToClone);
381 return rc;
382}
383RT_EXPORT_SYMBOL(RTEnvClone);
384
385
386RTDECL(int) RTEnvCloneUtf16Block(PRTENV phEnv, PCRTUTF16 pwszzBlock, uint32_t fFlags)
387{
388 AssertPtrReturn(pwszzBlock, VERR_INVALID_POINTER);
389 AssertReturn(!fFlags, VERR_INVALID_FLAGS);
390
391 /*
392 * Count the number of variables in the block.
393 */
394 uint32_t cVars = 0;
395 PCRTUTF16 pwsz = pwszzBlock;
396 while (*pwsz != '\0')
397 {
398 cVars++;
399 pwsz += RTUtf16Len(pwsz) + 1;
400 AssertReturn(cVars < _256K, VERR_OUT_OF_RANGE);
401 }
402
403 /*
404 * Create the duplicate.
405 */
406 PRTENVINTERNAL pIntEnv;
407#ifdef RTENV_ALLOW_EQUAL_FIRST_IN_VAR
408 int rc = rtEnvCreate(&pIntEnv, cVars + 1 /* NULL */, false /*fCaseSensitive*/, false /*fPutEnvBlock*/, true /*fFirstEqual*/);
409#else
410 int rc = rtEnvCreate(&pIntEnv, cVars + 1 /* NULL */, false /*fCaseSensitive*/, false /*fPutEnvBlock*/, false /*fFirstEqual*/);
411#endif
412 if (RT_SUCCESS(rc))
413 {
414 pIntEnv->cVars = cVars;
415 pIntEnv->papszEnv[pIntEnv->cVars] = NULL;
416
417 size_t iDst = 0;
418 for (pwsz = pwszzBlock; *pwsz != '\0'; pwsz += RTUtf16Len(pwsz) + 1)
419 {
420 int rc2 = RTUtf16ToUtf8(pwsz, &pIntEnv->papszEnv[iDst]);
421 if (RT_SUCCESS(rc2))
422 {
423 /* Make sure it contains an '='. */
424 const char *pszEqual = strchr(pIntEnv->papszEnv[iDst], '=');
425 if (!pszEqual)
426 {
427 rc2 = RTStrAAppend(&pIntEnv->papszEnv[iDst], "=");
428 if (RT_SUCCESS(rc2))
429 pszEqual = strchr(pIntEnv->papszEnv[iDst], '=');
430
431 }
432 if (pszEqual)
433 {
434 /* Check for duplicates, keep the last version. */
435 const char *pchVar = pIntEnv->papszEnv[iDst];
436 size_t cchVarNmAndEq = pszEqual - pchVar;
437 for (size_t iDst2 = 0; iDst2 < iDst; iDst2++)
438 if (pIntEnv->pfnCompare(pIntEnv->papszEnv[iDst2], pchVar, cchVarNmAndEq) == 0)
439 {
440 RTStrFree(pIntEnv->papszEnv[iDst2]);
441 pIntEnv->papszEnv[iDst2] = pIntEnv->papszEnv[iDst];
442 pIntEnv->papszEnv[iDst] = NULL;
443 iDst--;
444 break;
445 }
446 iDst++;
447 continue;
448 }
449 iDst++;
450 }
451
452 /* failed fatally. */
453 pIntEnv->cVars = iDst;
454 RTEnvDestroy(pIntEnv);
455 return rc2;
456 }
457 Assert(iDst <= pIntEnv->cVars);
458 pIntEnv->cVars = iDst;
459
460 /* done */
461 *phEnv = pIntEnv;
462 }
463 return rc;
464}
465RT_EXPORT_SYMBOL(RTEnvCloneUtf16Block);
466
467
468
469RTDECL(int) RTEnvReset(RTENV hEnv)
470{
471 PRTENVINTERNAL pIntEnv = hEnv;
472 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
473 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
474
475 RTENV_LOCK(pIntEnv);
476
477 size_t iVar = pIntEnv->cVars;
478 pIntEnv->cVars = 0;
479 while (iVar-- > 0)
480 {
481 RTMemFree(pIntEnv->papszEnv[iVar]);
482 pIntEnv->papszEnv[iVar] = NULL;
483 }
484
485 RTENV_UNLOCK(pIntEnv);
486 return VINF_SUCCESS;
487}
488RT_EXPORT_SYMBOL(RTEnvReset);
489
490
491/**
492 * Appends an already allocated string to papszEnv.
493 *
494 * @returns IPRT status code
495 * @param pIntEnv The environment block to append it to.
496 * @param pszEntry The string to add. Already duplicated, caller
497 * does error cleanup.
498 */
499static int rtEnvIntAppend(PRTENVINTERNAL pIntEnv, char *pszEntry)
500{
501 /*
502 * Do we need to resize the array?
503 */
504 int rc = VINF_SUCCESS;
505 size_t iVar = pIntEnv->cVars;
506 if (iVar + 2 > pIntEnv->cAllocated)
507 {
508 void *pvNew = RTMemRealloc(pIntEnv->papszEnv, sizeof(char *) * (pIntEnv->cAllocated + RTENV_GROW_SIZE));
509 if (!pvNew)
510 rc = VERR_NO_MEMORY;
511 else
512 {
513 pIntEnv->papszEnv = (char **)pvNew;
514 pIntEnv->cAllocated += RTENV_GROW_SIZE;
515 for (size_t iNewVar = pIntEnv->cVars; iNewVar < pIntEnv->cAllocated; iNewVar++)
516 pIntEnv->papszEnv[iNewVar] = NULL;
517 }
518 }
519 if (RT_SUCCESS(rc))
520 {
521 /*
522 * Append it.
523 */
524 pIntEnv->papszEnv[iVar] = pszEntry;
525 pIntEnv->papszEnv[iVar + 1] = NULL; /* this isn't really necessary, but doesn't hurt. */
526 pIntEnv->cVars = iVar + 1;
527 }
528 return rc;
529}
530
531
532/**
533 * Worker for RTEnvSetEx and RTEnvPutEx.
534 */
535static int rtEnvSetExWorker(RTENV Env, const char *pchVar, size_t cchVar, const char *pszValue)
536{
537 int rc;
538 if (Env == RTENV_DEFAULT)
539 {
540#ifdef RT_OS_WINDOWS
541 extern int rtEnvSetUtf8Worker(const char *pchVar, size_t cchVar, const char *pszValue);
542 rc = rtEnvSetUtf8Worker(pchVar, cchVar, pszValue);
543#else
544 /*
545 * Since RTEnvPut isn't UTF-8 clean and actually expects the strings
546 * to be in the current code page (codeset), we'll do the necessary
547 * conversions here.
548 */
549 char *pszVarOtherCP;
550 rc = RTStrUtf8ToCurrentCPEx(&pszVarOtherCP, pchVar, cchVar);
551 if (RT_SUCCESS(rc))
552 {
553 char *pszValueOtherCP;
554 rc = RTStrUtf8ToCurrentCP(&pszValueOtherCP, pszValue);
555 if (RT_SUCCESS(rc))
556 {
557 rc = RTEnvSet(pszVarOtherCP, pszValueOtherCP);
558 RTStrFree(pszValueOtherCP);
559 }
560 RTStrFree(pszVarOtherCP);
561 }
562#endif
563 }
564 else
565 {
566 PRTENVINTERNAL pIntEnv = Env;
567 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
568 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
569
570 /*
571 * Create the variable string.
572 */
573 const size_t cchValue = strlen(pszValue);
574 char *pszEntry = (char *)RTMemAlloc(cchVar + cchValue + 2);
575 if (pszEntry)
576 {
577 memcpy(pszEntry, pchVar, cchVar);
578 pszEntry[cchVar] = '=';
579 memcpy(&pszEntry[cchVar + 1], pszValue, cchValue + 1);
580
581 RTENV_LOCK(pIntEnv);
582
583 /*
584 * Find the location of the variable. (iVar = cVars if new)
585 */
586 rc = VINF_SUCCESS;
587 size_t iVar;
588 for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
589 if ( !pIntEnv->pfnCompare(pIntEnv->papszEnv[iVar], pchVar, cchVar)
590 && ( pIntEnv->papszEnv[iVar][cchVar] == '='
591 || pIntEnv->papszEnv[iVar][cchVar] == '\0') )
592 break;
593 if (iVar < pIntEnv->cVars)
594 {
595 /*
596 * Replace the current entry. Simple.
597 */
598 RTMemFree(pIntEnv->papszEnv[iVar]);
599 pIntEnv->papszEnv[iVar] = pszEntry;
600 }
601 else
602 {
603 /*
604 * New variable, append it.
605 */
606 Assert(pIntEnv->cVars == iVar);
607 rc = rtEnvIntAppend(pIntEnv, pszEntry);
608 }
609
610 RTENV_UNLOCK(pIntEnv);
611
612 if (RT_FAILURE(rc))
613 RTMemFree(pszEntry);
614 }
615 else
616 rc = VERR_NO_MEMORY;
617 }
618 return rc;
619}
620
621
622RTDECL(int) RTEnvSetEx(RTENV Env, const char *pszVar, const char *pszValue)
623{
624 AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
625 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
626 size_t const cchVar = strlen(pszVar);
627 AssertReturn(cchVar > 0, VERR_ENV_INVALID_VAR_NAME);
628#ifdef RTENV_ALLOW_EQUAL_FIRST_IN_VAR
629 char const *pszEq = (char const *)memchr(pszVar, '=', cchVar);
630 if (!pszEq)
631 { /* likely */ }
632 else
633 {
634 AssertReturn(Env != RTENV_DEFAULT, VERR_ENV_INVALID_VAR_NAME);
635 PRTENVINTERNAL pIntEnv = Env;
636 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
637 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
638 AssertReturn(pIntEnv->fFirstEqual, VERR_ENV_INVALID_VAR_NAME);
639 AssertReturn(memchr(pszVar + 1, '=', cchVar - 1) == NULL, VERR_ENV_INVALID_VAR_NAME);
640 }
641#else
642 AssertReturn(memchr(pszVar, '=', cchVar) == NULL, VERR_ENV_INVALID_VAR_NAME);
643#endif
644
645 return rtEnvSetExWorker(Env, pszVar, cchVar, pszValue);
646}
647RT_EXPORT_SYMBOL(RTEnvSetEx);
648
649
650RTDECL(int) RTEnvUnsetEx(RTENV Env, const char *pszVar)
651{
652 AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
653 AssertReturn(*pszVar, VERR_ENV_INVALID_VAR_NAME);
654
655 int rc;
656 if (Env == RTENV_DEFAULT)
657 {
658#ifdef RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API
659 rc = RTEnvUnsetUtf8(pszVar);
660#else
661 /*
662 * Since RTEnvUnset isn't UTF-8 clean and actually expects the strings
663 * to be in the current code page (codeset), we'll do the necessary
664 * conversions here.
665 */
666 char *pszVarOtherCP;
667 rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
668 if (RT_SUCCESS(rc))
669 {
670 rc = RTEnvUnset(pszVarOtherCP);
671 RTStrFree(pszVarOtherCP);
672 }
673#endif
674 }
675 else
676 {
677 PRTENVINTERNAL pIntEnv = Env;
678 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
679 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
680 const size_t cchVar = strlen(pszVar);
681 AssertReturn(cchVar > 0, VERR_ENV_INVALID_VAR_NAME);
682 AssertReturn(strchr(pIntEnv->fFirstEqual ? pszVar + 1 : pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
683
684 RTENV_LOCK(pIntEnv);
685
686 /*
687 * Remove all variable by the given name.
688 */
689 rc = VINF_ENV_VAR_NOT_FOUND;
690 size_t iVar;
691 for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
692 if ( !pIntEnv->pfnCompare(pIntEnv->papszEnv[iVar], pszVar, cchVar)
693 && ( pIntEnv->papszEnv[iVar][cchVar] == '='
694 || pIntEnv->papszEnv[iVar][cchVar] == '\0') )
695 {
696 if (!pIntEnv->fPutEnvBlock)
697 {
698 RTMemFree(pIntEnv->papszEnv[iVar]);
699 pIntEnv->cVars--;
700 if (pIntEnv->cVars > 0)
701 pIntEnv->papszEnv[iVar] = pIntEnv->papszEnv[pIntEnv->cVars];
702 pIntEnv->papszEnv[pIntEnv->cVars] = NULL;
703 }
704 else
705 {
706 /* Record this unset by keeping the variable without any equal sign. */
707 pIntEnv->papszEnv[iVar][cchVar] = '\0';
708 }
709 rc = VINF_SUCCESS;
710 /* no break, there could be more. */
711 }
712
713 /*
714 * If this is a change record, we may need to add it.
715 */
716 if (rc == VINF_ENV_VAR_NOT_FOUND && pIntEnv->fPutEnvBlock)
717 {
718 char *pszEntry = (char *)RTMemDup(pszVar, cchVar + 1);
719 if (pszEntry)
720 {
721 rc = rtEnvIntAppend(pIntEnv, pszEntry);
722 if (RT_SUCCESS(rc))
723 rc = VINF_ENV_VAR_NOT_FOUND;
724 else
725 RTMemFree(pszEntry);
726 }
727 else
728 rc = VERR_NO_MEMORY;
729 }
730
731 RTENV_UNLOCK(pIntEnv);
732 }
733 return rc;
734
735}
736RT_EXPORT_SYMBOL(RTEnvUnsetEx);
737
738
739RTDECL(int) RTEnvPutEx(RTENV Env, const char *pszVarEqualValue)
740{
741 int rc;
742 AssertPtrReturn(pszVarEqualValue, VERR_INVALID_POINTER);
743 const char *pszEq = strchr(pszVarEqualValue, '=');
744#ifdef RTENV_ALLOW_EQUAL_FIRST_IN_VAR
745 if ( pszEq == pszVarEqualValue
746 && Env != RTENV_DEFAULT)
747 {
748 PRTENVINTERNAL pIntEnv = Env;
749 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
750 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
751 if (pIntEnv->fFirstEqual)
752 pszEq = strchr(pszVarEqualValue + 1, '=');
753 }
754#endif
755 if (!pszEq)
756 rc = RTEnvUnsetEx(Env, pszVarEqualValue);
757 else
758 {
759 AssertReturn(pszEq != pszVarEqualValue, VERR_ENV_INVALID_VAR_NAME);
760 rc = rtEnvSetExWorker(Env, pszVarEqualValue, pszEq - pszVarEqualValue, pszEq + 1);
761 }
762 return rc;
763}
764RT_EXPORT_SYMBOL(RTEnvPutEx);
765
766
767RTDECL(int) RTEnvGetEx(RTENV Env, const char *pszVar, char *pszValue, size_t cbValue, size_t *pcchActual)
768{
769 AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
770 AssertPtrNullReturn(pszValue, VERR_INVALID_POINTER);
771 AssertPtrNullReturn(pcchActual, VERR_INVALID_POINTER);
772 AssertReturn(pcchActual || (pszValue && cbValue), VERR_INVALID_PARAMETER);
773
774 if (pcchActual)
775 *pcchActual = 0;
776 int rc;
777 if (Env == RTENV_DEFAULT)
778 {
779#ifdef RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API
780 rc = RTEnvGetUtf8(pszVar, pszValue, cbValue, pcchActual);
781#else
782 /*
783 * Since RTEnvGet isn't UTF-8 clean and actually expects the strings
784 * to be in the current code page (codeset), we'll do the necessary
785 * conversions here.
786 */
787 char *pszVarOtherCP;
788 rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
789 if (RT_SUCCESS(rc))
790 {
791 const char *pszValueOtherCP = RTEnvGet(pszVarOtherCP);
792 RTStrFree(pszVarOtherCP);
793 if (pszValueOtherCP)
794 {
795 char *pszValueUtf8;
796 rc = RTStrCurrentCPToUtf8(&pszValueUtf8, pszValueOtherCP);
797 if (RT_SUCCESS(rc))
798 {
799 rc = VINF_SUCCESS;
800 size_t cch = strlen(pszValueUtf8);
801 if (pcchActual)
802 *pcchActual = cch;
803 if (pszValue && cbValue)
804 {
805 if (cch < cbValue)
806 memcpy(pszValue, pszValueUtf8, cch + 1);
807 else
808 rc = VERR_BUFFER_OVERFLOW;
809 }
810 RTStrFree(pszValueUtf8);
811 }
812 }
813 else
814 rc = VERR_ENV_VAR_NOT_FOUND;
815 }
816#endif
817 }
818 else
819 {
820 PRTENVINTERNAL pIntEnv = Env;
821 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
822 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
823 const size_t cchVar = strlen(pszVar);
824 AssertReturn(cchVar > 0, VERR_ENV_INVALID_VAR_NAME);
825 AssertReturn(strchr(pIntEnv->fFirstEqual ? pszVar + 1 : pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
826
827 RTENV_LOCK(pIntEnv);
828
829 /*
830 * Locate the first variable and return it to the caller.
831 */
832 rc = VERR_ENV_VAR_NOT_FOUND;
833 size_t iVar;
834 for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
835 if (!pIntEnv->pfnCompare(pIntEnv->papszEnv[iVar], pszVar, cchVar))
836 {
837 if (pIntEnv->papszEnv[iVar][cchVar] == '=')
838 {
839 rc = VINF_SUCCESS;
840 const char *pszValueOrg = pIntEnv->papszEnv[iVar] + cchVar + 1;
841 size_t cch = strlen(pszValueOrg);
842 if (pcchActual)
843 *pcchActual = cch;
844 if (pszValue && cbValue)
845 {
846 if (cch < cbValue)
847 memcpy(pszValue, pszValueOrg, cch + 1);
848 else
849 rc = VERR_BUFFER_OVERFLOW;
850 }
851 break;
852 }
853 if (pIntEnv->papszEnv[iVar][cchVar] == '\0')
854 {
855 Assert(pIntEnv->fPutEnvBlock);
856 rc = VERR_ENV_VAR_UNSET;
857 break;
858 }
859 }
860
861 RTENV_UNLOCK(pIntEnv);
862 }
863 return rc;
864}
865RT_EXPORT_SYMBOL(RTEnvGetEx);
866
867
868RTDECL(bool) RTEnvExistEx(RTENV Env, const char *pszVar)
869{
870 AssertPtrReturn(pszVar, false);
871
872 bool fExists = false;
873 if (Env == RTENV_DEFAULT)
874 {
875#ifdef RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API
876 fExists = RTEnvExistsUtf8(pszVar);
877#else
878 /*
879 * Since RTEnvExist isn't UTF-8 clean and actually expects the strings
880 * to be in the current code page (codeset), we'll do the necessary
881 * conversions here.
882 */
883 char *pszVarOtherCP;
884 int rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
885 if (RT_SUCCESS(rc))
886 {
887 fExists = RTEnvExist(pszVarOtherCP);
888 RTStrFree(pszVarOtherCP);
889 }
890#endif
891 }
892 else
893 {
894 PRTENVINTERNAL pIntEnv = Env;
895 AssertPtrReturn(pIntEnv, false);
896 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, false);
897 const size_t cchVar = strlen(pszVar);
898 AssertReturn(cchVar > 0, false);
899 AssertReturn(strchr(pIntEnv->fFirstEqual ? pszVar + 1 : pszVar, '=') == NULL, false);
900
901 RTENV_LOCK(pIntEnv);
902
903 /*
904 * Simple search.
905 */
906 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
907 if (!pIntEnv->pfnCompare(pIntEnv->papszEnv[iVar], pszVar, cchVar))
908 {
909 if (pIntEnv->papszEnv[iVar][cchVar] == '=')
910 {
911 fExists = true;
912 break;
913 }
914 if (pIntEnv->papszEnv[iVar][cchVar] == '\0')
915 break;
916 }
917
918 RTENV_UNLOCK(pIntEnv);
919 }
920 return fExists;
921}
922RT_EXPORT_SYMBOL(RTEnvExistEx);
923
924
925RTDECL(char const * const *) RTEnvGetExecEnvP(RTENV Env)
926{
927 const char * const *papszRet;
928 if (Env == RTENV_DEFAULT)
929 {
930 /** @todo fix this API it's fundamentally wrong! */
931 papszRet = rtEnvDefault();
932 if (!papszRet)
933 {
934 static const char * const s_papszDummy[2] = { NULL, NULL };
935 papszRet = &s_papszDummy[0];
936 }
937 }
938 else
939 {
940 PRTENVINTERNAL pIntEnv = Env;
941 AssertPtrReturn(pIntEnv, NULL);
942 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, NULL);
943
944 RTENV_LOCK(pIntEnv);
945
946 /*
947 * Free any old envp.
948 */
949 if (pIntEnv->papszEnvOtherCP)
950 {
951 for (size_t iVar = 0; pIntEnv->papszEnvOtherCP[iVar]; iVar++)
952 {
953 RTStrFree(pIntEnv->papszEnvOtherCP[iVar]);
954 pIntEnv->papszEnvOtherCP[iVar] = NULL;
955 }
956 RTMemFree(pIntEnv->papszEnvOtherCP);
957 pIntEnv->papszEnvOtherCP = NULL;
958 }
959
960 /*
961 * Construct a new envp with the strings in the process code set.
962 */
963 char **papsz;
964 papszRet = pIntEnv->papszEnvOtherCP = papsz = (char **)RTMemAlloc(sizeof(char *) * (pIntEnv->cVars + 1));
965 if (papsz)
966 {
967 papsz[pIntEnv->cVars] = NULL;
968 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
969 {
970 int rc = RTStrUtf8ToCurrentCP(&papsz[iVar], pIntEnv->papszEnv[iVar]);
971 if (RT_FAILURE(rc))
972 {
973 /* RTEnvDestroy / we cleans up later. */
974 papsz[iVar] = NULL;
975 AssertRC(rc);
976 papszRet = NULL;
977 break;
978 }
979 }
980 }
981
982 RTENV_UNLOCK(pIntEnv);
983 }
984 return papszRet;
985}
986RT_EXPORT_SYMBOL(RTEnvGetExecEnvP);
987
988
989/**
990 * RTSort callback for comparing two environment variables.
991 *
992 * @returns -1, 0, 1. See PFNRTSORTCMP.
993 * @param pvElement1 Variable 1.
994 * @param pvElement2 Variable 2.
995 * @param pvUser Ignored.
996 */
997static DECLCALLBACK(int) rtEnvSortCompare(const void *pvElement1, const void *pvElement2, void *pvUser)
998{
999 NOREF(pvUser);
1000 int iDiff = strcmp((const char *)pvElement1, (const char *)pvElement2);
1001 if (iDiff < 0)
1002 iDiff = -1;
1003 else if (iDiff > 0)
1004 iDiff = 1;
1005 return iDiff;
1006}
1007
1008
1009RTDECL(int) RTEnvQueryUtf16Block(RTENV hEnv, PRTUTF16 *ppwszzBlock)
1010{
1011 RTENV hClone = NIL_RTENV;
1012 PRTENVINTERNAL pIntEnv;
1013 int rc;
1014
1015 /*
1016 * Validate / simplify input.
1017 */
1018 if (hEnv == RTENV_DEFAULT)
1019 {
1020 rc = RTEnvClone(&hClone, RTENV_DEFAULT);
1021 if (RT_FAILURE(rc))
1022 return rc;
1023 pIntEnv = hClone;
1024 }
1025 else
1026 {
1027 pIntEnv = hEnv;
1028 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
1029 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
1030 rc = VINF_SUCCESS;
1031 }
1032
1033 RTENV_LOCK(pIntEnv);
1034
1035 /*
1036 * Sort it first.
1037 */
1038 RTSortApvShell((void **)pIntEnv->papszEnv, pIntEnv->cVars, rtEnvSortCompare, pIntEnv);
1039
1040 /*
1041 * Calculate the size.
1042 */
1043 size_t cwc;
1044 size_t cwcTotal = 2;
1045 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
1046 {
1047 rc = RTStrCalcUtf16LenEx(pIntEnv->papszEnv[iVar], RTSTR_MAX, &cwc);
1048 AssertRCBreak(rc);
1049 cwcTotal += cwc + 1;
1050 }
1051
1052 PRTUTF16 pwszzBlock = NULL;
1053 if (RT_SUCCESS(rc))
1054 {
1055 /*
1056 * Perform the conversion.
1057 */
1058 PRTUTF16 pwszz = pwszzBlock = (PRTUTF16)RTMemAlloc(cwcTotal * sizeof(RTUTF16));
1059 if (pwszz)
1060 {
1061 size_t cwcLeft = cwcTotal;
1062 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
1063 {
1064 rc = RTStrToUtf16Ex(pIntEnv->papszEnv[iVar], RTSTR_MAX,
1065 &pwszz, cwcTotal - (pwszz - pwszzBlock), &cwc);
1066 AssertRCBreak(rc);
1067 pwszz += cwc + 1;
1068 cwcLeft -= cwc + 1;
1069 AssertBreakStmt(cwcLeft >= 2, rc = VERR_INTERNAL_ERROR_3);
1070 }
1071 AssertStmt(cwcLeft == 2 || RT_FAILURE(rc), rc = VERR_INTERNAL_ERROR_2);
1072 if (RT_SUCCESS(rc))
1073 {
1074 pwszz[0] = '\0';
1075 pwszz[1] = '\0';
1076 }
1077 else
1078 {
1079 RTMemFree(pwszzBlock);
1080 pwszzBlock = NULL;
1081 }
1082 }
1083 else
1084 rc = VERR_NO_MEMORY;
1085 }
1086
1087 RTENV_UNLOCK(pIntEnv);
1088
1089 if (hClone != NIL_RTENV)
1090 RTEnvDestroy(hClone);
1091 if (RT_SUCCESS(rc))
1092 *ppwszzBlock = pwszzBlock;
1093 return rc;
1094}
1095RT_EXPORT_SYMBOL(RTEnvQueryUtf16Block);
1096
1097
1098RTDECL(void) RTEnvFreeUtf16Block(PRTUTF16 pwszzBlock)
1099{
1100 RTMemFree(pwszzBlock);
1101}
1102RT_EXPORT_SYMBOL(RTEnvFreeUtf16Block);
1103
1104
1105RTDECL(int) RTEnvQueryUtf8Block(RTENV hEnv, bool fSorted, char **ppszzBlock, size_t *pcbBlock)
1106{
1107 RTENV hClone = NIL_RTENV;
1108 PRTENVINTERNAL pIntEnv;
1109 int rc;
1110
1111 /*
1112 * Validate / simplify input.
1113 */
1114 if (hEnv == RTENV_DEFAULT)
1115 {
1116 rc = RTEnvClone(&hClone, RTENV_DEFAULT);
1117 if (RT_FAILURE(rc))
1118 return rc;
1119 pIntEnv = hClone;
1120 }
1121 else
1122 {
1123 pIntEnv = hEnv;
1124 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
1125 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
1126 rc = VINF_SUCCESS;
1127 }
1128
1129 RTENV_LOCK(pIntEnv);
1130
1131 /*
1132 * Sort it, if requested.
1133 */
1134 if (fSorted)
1135 RTSortApvShell((void **)pIntEnv->papszEnv, pIntEnv->cVars, rtEnvSortCompare, pIntEnv);
1136
1137 /*
1138 * Calculate the size. We add one extra terminator just to be on the safe side.
1139 */
1140 size_t cbBlock = 2;
1141 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
1142 cbBlock += strlen(pIntEnv->papszEnv[iVar]) + 1;
1143
1144 if (pcbBlock)
1145 *pcbBlock = cbBlock - 1;
1146
1147 /*
1148 * Allocate memory and copy out the variables.
1149 */
1150 char *pszzBlock;
1151 char *pszz = pszzBlock = (char *)RTMemAlloc(cbBlock);
1152 if (pszz)
1153 {
1154 size_t cbLeft = cbBlock;
1155 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
1156 {
1157 size_t cb = strlen(pIntEnv->papszEnv[iVar]) + 1;
1158 AssertBreakStmt(cb + 2 <= cbLeft, rc = VERR_INTERNAL_ERROR_3);
1159 memcpy(pszz, pIntEnv->papszEnv[iVar], cb);
1160 pszz += cb;
1161 cbLeft -= cb;
1162 }
1163 if (RT_SUCCESS(rc))
1164 {
1165 pszz[0] = '\0';
1166 pszz[1] = '\0'; /* The extra one. */
1167 }
1168 else
1169 {
1170 RTMemFree(pszzBlock);
1171 pszzBlock = NULL;
1172 }
1173 }
1174 else
1175 rc = VERR_NO_MEMORY;
1176
1177 RTENV_UNLOCK(pIntEnv);
1178
1179 if (hClone != NIL_RTENV)
1180 RTEnvDestroy(hClone);
1181 if (RT_SUCCESS(rc))
1182 *ppszzBlock = pszzBlock;
1183 return rc;
1184}
1185RT_EXPORT_SYMBOL(RTEnvQueryUtf8Block);
1186
1187
1188RTDECL(void) RTEnvFreeUtf8Block(char *pszzBlock)
1189{
1190 RTMemFree(pszzBlock);
1191}
1192RT_EXPORT_SYMBOL(RTEnvFreeUtf8Block);
1193
1194
1195RTDECL(uint32_t) RTEnvCountEx(RTENV hEnv)
1196{
1197 PRTENVINTERNAL pIntEnv = hEnv;
1198 AssertPtrReturn(pIntEnv, UINT32_MAX);
1199 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, UINT32_MAX);
1200
1201 RTENV_LOCK(pIntEnv);
1202 uint32_t cVars = (uint32_t)pIntEnv->cVars;
1203 RTENV_UNLOCK(pIntEnv);
1204
1205 return cVars;
1206}
1207RT_EXPORT_SYMBOL(RTEnvCountEx);
1208
1209
1210RTDECL(int) RTEnvGetByIndexEx(RTENV hEnv, uint32_t iVar, char *pszVar, size_t cbVar, char *pszValue, size_t cbValue)
1211{
1212 PRTENVINTERNAL pIntEnv = hEnv;
1213 AssertPtrReturn(pIntEnv, UINT32_MAX);
1214 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, UINT32_MAX);
1215 if (cbVar)
1216 AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
1217 if (cbValue)
1218 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
1219
1220 RTENV_LOCK(pIntEnv);
1221
1222 int rc;
1223 if (iVar < pIntEnv->cVars)
1224 {
1225 const char *pszSrcVar = pIntEnv->papszEnv[iVar];
1226 const char *pszSrcValue = strchr(pszSrcVar, '=');
1227 bool fHasEqual = pszSrcValue != NULL;
1228 if (pszSrcValue)
1229 {
1230 pszSrcValue++;
1231 rc = VINF_SUCCESS;
1232 }
1233 else
1234 {
1235 pszSrcValue = strchr(pszSrcVar, '\0');
1236 rc = VINF_ENV_VAR_UNSET;
1237 }
1238 if (cbVar)
1239 {
1240 int rc2 = RTStrCopyEx(pszVar, cbVar, pszSrcVar, pszSrcValue - pszSrcVar - fHasEqual);
1241 if (RT_FAILURE(rc2))
1242 rc = rc2;
1243 }
1244 if (cbValue)
1245 {
1246 int rc2 = RTStrCopy(pszValue, cbValue, pszSrcValue);
1247 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1248 rc = rc2;
1249 }
1250 }
1251 else
1252 rc = VERR_ENV_VAR_NOT_FOUND;
1253
1254 RTENV_UNLOCK(pIntEnv);
1255
1256 return rc;
1257}
1258RT_EXPORT_SYMBOL(RTEnvGetByIndexEx);
1259
1260
1261RTDECL(const char *) RTEnvGetByIndexRawEx(RTENV hEnv, uint32_t iVar)
1262{
1263 PRTENVINTERNAL pIntEnv = hEnv;
1264 AssertPtrReturn(pIntEnv, NULL);
1265 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, NULL);
1266
1267 RTENV_LOCK(pIntEnv);
1268
1269 const char *pszRet;
1270 if (iVar < pIntEnv->cVars)
1271 pszRet = pIntEnv->papszEnv[iVar];
1272 else
1273 pszRet = NULL;
1274
1275 RTENV_UNLOCK(pIntEnv);
1276
1277 return pszRet;
1278}
1279RT_EXPORT_SYMBOL(RTEnvGetByIndexRawEx);
1280
1281
1282RTDECL(int) RTEnvCreateChangeRecord(PRTENV phEnv)
1283{
1284 AssertPtrReturn(phEnv, VERR_INVALID_POINTER);
1285#ifdef RTENV_ALLOW_EQUAL_FIRST_IN_VAR
1286 return rtEnvCreate(phEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, true /*fPutEnvBlock*/, true /*fFirstEqual*/);
1287#else
1288 return rtEnvCreate(phEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, true /*fPutEnvBlock*/, false /*fFirstEqual*/);
1289#endif
1290}
1291RT_EXPORT_SYMBOL(RTEnvCreateChangeRecord);
1292
1293
1294RTDECL(int) RTEnvCreateChangeRecordEx(PRTENV phEnv, uint32_t fFlags)
1295{
1296 AssertPtrReturn(phEnv, VERR_INVALID_POINTER);
1297 AssertReturn(!(fFlags & ~RTENV_CREATE_F_VALID_MASK), VERR_INVALID_FLAGS);
1298 return rtEnvCreate(phEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, true /*fPutEnvBlock*/,
1299 RT_BOOL(fFlags & RTENV_CREATE_F_ALLOW_EQUAL_FIRST_IN_VAR));
1300}
1301RT_EXPORT_SYMBOL(RTEnvCreateChangeRecord);
1302
1303
1304RTDECL(bool) RTEnvIsChangeRecord(RTENV hEnv)
1305{
1306 if (hEnv == RTENV_DEFAULT)
1307 return false;
1308
1309 PRTENVINTERNAL pIntEnv = hEnv;
1310 AssertPtrReturn(pIntEnv, false);
1311 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, false);
1312 return pIntEnv->fPutEnvBlock;
1313}
1314RT_EXPORT_SYMBOL(RTEnvIsChangeRecord);
1315
1316
1317RTDECL(int) RTEnvApplyChanges(RTENV hEnvDst, RTENV hEnvChanges)
1318{
1319 PRTENVINTERNAL pIntEnvChanges = hEnvChanges;
1320 AssertPtrReturn(pIntEnvChanges, VERR_INVALID_HANDLE);
1321 AssertReturn(pIntEnvChanges->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
1322
1323 /** @todo lock validator trouble ahead here! */
1324 RTENV_LOCK(pIntEnvChanges);
1325
1326 int rc = VINF_SUCCESS;
1327 for (uint32_t iChange = 0; iChange < pIntEnvChanges->cVars && RT_SUCCESS(rc); iChange++)
1328 rc = RTEnvPutEx(hEnvDst, pIntEnvChanges->papszEnv[iChange]);
1329
1330 RTENV_UNLOCK(pIntEnvChanges);
1331
1332 return rc;
1333}
1334RT_EXPORT_SYMBOL(RTEnvApplyChanges);
1335
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette