VirtualBox

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

Last change on this file since 89264 was 85121, checked in by vboxsync, 4 years ago

iprt/cdefs.h: Refactored the typedef use of DECLCALLBACK as well as DECLCALLBACKMEMBER to wrap the whole expression, similar to the DECLR?CALLBACKMEMBER macros. This allows adding a throw() at the end when compiling with the VC++ compiler to indicate that the callbacks won't throw anything, so we can stop supressing the C5039 warning about passing functions that can potential throw C++ exceptions to extern C code that can't necessarily cope with such (unwind,++). Introduced a few _EX variations that allows specifying different/no calling convention too, as that's handy when dynamically resolving host APIs. Fixed numerous places missing DECLCALLBACK and such. Left two angry @todos regarding use of CreateThread. bugref:9794

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 40.5 KB
Line 
1/* $Id: env-generic.cpp 85121 2020-07-08 19:33:26Z vboxsync $ */
2/** @file
3 * IPRT - Environment, Generic.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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 by default. This is used by
84 * windows to 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 char const *pszEq = (char const *)memchr(pszVar, '=', cchVar);
629 if (!pszEq)
630 { /* likely */ }
631 else
632 {
633 AssertReturn(Env != RTENV_DEFAULT, VERR_ENV_INVALID_VAR_NAME);
634 PRTENVINTERNAL pIntEnv = Env;
635 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
636 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
637 AssertReturn(pIntEnv->fFirstEqual, VERR_ENV_INVALID_VAR_NAME);
638 AssertReturn(memchr(pszVar + 1, '=', cchVar - 1) == NULL, VERR_ENV_INVALID_VAR_NAME);
639 }
640
641 return rtEnvSetExWorker(Env, pszVar, cchVar, pszValue);
642}
643RT_EXPORT_SYMBOL(RTEnvSetEx);
644
645
646RTDECL(int) RTEnvUnsetEx(RTENV Env, const char *pszVar)
647{
648 AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
649 AssertReturn(*pszVar, VERR_ENV_INVALID_VAR_NAME);
650
651 int rc;
652 if (Env == RTENV_DEFAULT)
653 {
654#ifdef RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API
655 rc = RTEnvUnsetUtf8(pszVar);
656#else
657 /*
658 * Since RTEnvUnset isn't UTF-8 clean and actually expects the strings
659 * to be in the current code page (codeset), we'll do the necessary
660 * conversions here.
661 */
662 char *pszVarOtherCP;
663 rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
664 if (RT_SUCCESS(rc))
665 {
666 rc = RTEnvUnset(pszVarOtherCP);
667 RTStrFree(pszVarOtherCP);
668 }
669#endif
670 }
671 else
672 {
673 PRTENVINTERNAL pIntEnv = Env;
674 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
675 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
676 const size_t cchVar = strlen(pszVar);
677 AssertReturn(cchVar > 0, VERR_ENV_INVALID_VAR_NAME);
678 AssertReturn(strchr(pIntEnv->fFirstEqual ? pszVar + 1 : pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
679
680 RTENV_LOCK(pIntEnv);
681
682 /*
683 * Remove all variable by the given name.
684 */
685 rc = VINF_ENV_VAR_NOT_FOUND;
686 size_t iVar;
687 for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
688 if ( !pIntEnv->pfnCompare(pIntEnv->papszEnv[iVar], pszVar, cchVar)
689 && ( pIntEnv->papszEnv[iVar][cchVar] == '='
690 || pIntEnv->papszEnv[iVar][cchVar] == '\0') )
691 {
692 if (!pIntEnv->fPutEnvBlock)
693 {
694 RTMemFree(pIntEnv->papszEnv[iVar]);
695 pIntEnv->cVars--;
696 if (pIntEnv->cVars > 0)
697 pIntEnv->papszEnv[iVar] = pIntEnv->papszEnv[pIntEnv->cVars];
698 pIntEnv->papszEnv[pIntEnv->cVars] = NULL;
699 }
700 else
701 {
702 /* Record this unset by keeping the variable without any equal sign. */
703 pIntEnv->papszEnv[iVar][cchVar] = '\0';
704 }
705 rc = VINF_SUCCESS;
706 /* no break, there could be more. */
707 }
708
709 /*
710 * If this is a change record, we may need to add it.
711 */
712 if (rc == VINF_ENV_VAR_NOT_FOUND && pIntEnv->fPutEnvBlock)
713 {
714 char *pszEntry = (char *)RTMemDup(pszVar, cchVar + 1);
715 if (pszEntry)
716 {
717 rc = rtEnvIntAppend(pIntEnv, pszEntry);
718 if (RT_SUCCESS(rc))
719 rc = VINF_ENV_VAR_NOT_FOUND;
720 else
721 RTMemFree(pszEntry);
722 }
723 else
724 rc = VERR_NO_MEMORY;
725 }
726
727 RTENV_UNLOCK(pIntEnv);
728 }
729 return rc;
730
731}
732RT_EXPORT_SYMBOL(RTEnvUnsetEx);
733
734
735RTDECL(int) RTEnvPutEx(RTENV Env, const char *pszVarEqualValue)
736{
737 int rc;
738 AssertPtrReturn(pszVarEqualValue, VERR_INVALID_POINTER);
739 const char *pszEq = strchr(pszVarEqualValue, '=');
740 if ( pszEq == pszVarEqualValue
741 && Env != RTENV_DEFAULT)
742 {
743 PRTENVINTERNAL pIntEnv = Env;
744 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
745 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
746 if (pIntEnv->fFirstEqual)
747 pszEq = strchr(pszVarEqualValue + 1, '=');
748 }
749 if (!pszEq)
750 rc = RTEnvUnsetEx(Env, pszVarEqualValue);
751 else
752 {
753 AssertReturn(pszEq != pszVarEqualValue, VERR_ENV_INVALID_VAR_NAME);
754 rc = rtEnvSetExWorker(Env, pszVarEqualValue, pszEq - pszVarEqualValue, pszEq + 1);
755 }
756 return rc;
757}
758RT_EXPORT_SYMBOL(RTEnvPutEx);
759
760
761RTDECL(int) RTEnvGetEx(RTENV Env, const char *pszVar, char *pszValue, size_t cbValue, size_t *pcchActual)
762{
763 AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
764 AssertPtrNullReturn(pszValue, VERR_INVALID_POINTER);
765 AssertPtrNullReturn(pcchActual, VERR_INVALID_POINTER);
766 AssertReturn(pcchActual || (pszValue && cbValue), VERR_INVALID_PARAMETER);
767
768 if (pcchActual)
769 *pcchActual = 0;
770 int rc;
771 if (Env == RTENV_DEFAULT)
772 {
773#ifdef RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API
774 rc = RTEnvGetUtf8(pszVar, pszValue, cbValue, pcchActual);
775#else
776 /*
777 * Since RTEnvGet isn't UTF-8 clean and actually expects the strings
778 * to be in the current code page (codeset), we'll do the necessary
779 * conversions here.
780 */
781 char *pszVarOtherCP;
782 rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
783 if (RT_SUCCESS(rc))
784 {
785 const char *pszValueOtherCP = RTEnvGet(pszVarOtherCP);
786 RTStrFree(pszVarOtherCP);
787 if (pszValueOtherCP)
788 {
789 char *pszValueUtf8;
790 rc = RTStrCurrentCPToUtf8(&pszValueUtf8, pszValueOtherCP);
791 if (RT_SUCCESS(rc))
792 {
793 rc = VINF_SUCCESS;
794 size_t cch = strlen(pszValueUtf8);
795 if (pcchActual)
796 *pcchActual = cch;
797 if (pszValue && cbValue)
798 {
799 if (cch < cbValue)
800 memcpy(pszValue, pszValueUtf8, cch + 1);
801 else
802 rc = VERR_BUFFER_OVERFLOW;
803 }
804 RTStrFree(pszValueUtf8);
805 }
806 }
807 else
808 rc = VERR_ENV_VAR_NOT_FOUND;
809 }
810#endif
811 }
812 else
813 {
814 PRTENVINTERNAL pIntEnv = Env;
815 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
816 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
817 const size_t cchVar = strlen(pszVar);
818 AssertReturn(cchVar > 0, VERR_ENV_INVALID_VAR_NAME);
819 AssertReturn(strchr(pIntEnv->fFirstEqual ? pszVar + 1 : pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
820
821 RTENV_LOCK(pIntEnv);
822
823 /*
824 * Locate the first variable and return it to the caller.
825 */
826 rc = VERR_ENV_VAR_NOT_FOUND;
827 size_t iVar;
828 for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
829 if (!pIntEnv->pfnCompare(pIntEnv->papszEnv[iVar], pszVar, cchVar))
830 {
831 if (pIntEnv->papszEnv[iVar][cchVar] == '=')
832 {
833 rc = VINF_SUCCESS;
834 const char *pszValueOrg = pIntEnv->papszEnv[iVar] + cchVar + 1;
835 size_t cch = strlen(pszValueOrg);
836 if (pcchActual)
837 *pcchActual = cch;
838 if (pszValue && cbValue)
839 {
840 if (cch < cbValue)
841 memcpy(pszValue, pszValueOrg, cch + 1);
842 else
843 rc = VERR_BUFFER_OVERFLOW;
844 }
845 break;
846 }
847 if (pIntEnv->papszEnv[iVar][cchVar] == '\0')
848 {
849 Assert(pIntEnv->fPutEnvBlock);
850 rc = VERR_ENV_VAR_UNSET;
851 break;
852 }
853 }
854
855 RTENV_UNLOCK(pIntEnv);
856 }
857 return rc;
858}
859RT_EXPORT_SYMBOL(RTEnvGetEx);
860
861
862RTDECL(bool) RTEnvExistEx(RTENV Env, const char *pszVar)
863{
864 AssertPtrReturn(pszVar, false);
865
866 bool fExists = false;
867 if (Env == RTENV_DEFAULT)
868 {
869#ifdef RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API
870 fExists = RTEnvExistsUtf8(pszVar);
871#else
872 /*
873 * Since RTEnvExist isn't UTF-8 clean and actually expects the strings
874 * to be in the current code page (codeset), we'll do the necessary
875 * conversions here.
876 */
877 char *pszVarOtherCP;
878 int rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
879 if (RT_SUCCESS(rc))
880 {
881 fExists = RTEnvExist(pszVarOtherCP);
882 RTStrFree(pszVarOtherCP);
883 }
884#endif
885 }
886 else
887 {
888 PRTENVINTERNAL pIntEnv = Env;
889 AssertPtrReturn(pIntEnv, false);
890 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, false);
891 const size_t cchVar = strlen(pszVar);
892 AssertReturn(cchVar > 0, false);
893 AssertReturn(strchr(pIntEnv->fFirstEqual ? pszVar + 1 : pszVar, '=') == NULL, false);
894
895 RTENV_LOCK(pIntEnv);
896
897 /*
898 * Simple search.
899 */
900 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
901 if (!pIntEnv->pfnCompare(pIntEnv->papszEnv[iVar], pszVar, cchVar))
902 {
903 if (pIntEnv->papszEnv[iVar][cchVar] == '=')
904 {
905 fExists = true;
906 break;
907 }
908 if (pIntEnv->papszEnv[iVar][cchVar] == '\0')
909 break;
910 }
911
912 RTENV_UNLOCK(pIntEnv);
913 }
914 return fExists;
915}
916RT_EXPORT_SYMBOL(RTEnvExistEx);
917
918
919RTDECL(char const * const *) RTEnvGetExecEnvP(RTENV Env)
920{
921 const char * const *papszRet;
922 if (Env == RTENV_DEFAULT)
923 {
924 /** @todo fix this API it's fundamentally wrong! */
925 papszRet = rtEnvDefault();
926 if (!papszRet)
927 {
928 static const char * const s_papszDummy[2] = { NULL, NULL };
929 papszRet = &s_papszDummy[0];
930 }
931 }
932 else
933 {
934 PRTENVINTERNAL pIntEnv = Env;
935 AssertPtrReturn(pIntEnv, NULL);
936 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, NULL);
937
938 RTENV_LOCK(pIntEnv);
939
940 /*
941 * Free any old envp.
942 */
943 if (pIntEnv->papszEnvOtherCP)
944 {
945 for (size_t iVar = 0; pIntEnv->papszEnvOtherCP[iVar]; iVar++)
946 {
947 RTStrFree(pIntEnv->papszEnvOtherCP[iVar]);
948 pIntEnv->papszEnvOtherCP[iVar] = NULL;
949 }
950 RTMemFree(pIntEnv->papszEnvOtherCP);
951 pIntEnv->papszEnvOtherCP = NULL;
952 }
953
954 /*
955 * Construct a new envp with the strings in the process code set.
956 */
957 char **papsz;
958 papszRet = pIntEnv->papszEnvOtherCP = papsz = (char **)RTMemAlloc(sizeof(char *) * (pIntEnv->cVars + 1));
959 if (papsz)
960 {
961 papsz[pIntEnv->cVars] = NULL;
962 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
963 {
964 int rc = RTStrUtf8ToCurrentCP(&papsz[iVar], pIntEnv->papszEnv[iVar]);
965 if (RT_FAILURE(rc))
966 {
967 /* RTEnvDestroy / we cleans up later. */
968 papsz[iVar] = NULL;
969 AssertRC(rc);
970 papszRet = NULL;
971 break;
972 }
973 }
974 }
975
976 RTENV_UNLOCK(pIntEnv);
977 }
978 return papszRet;
979}
980RT_EXPORT_SYMBOL(RTEnvGetExecEnvP);
981
982
983/**
984 * RTSort callback for comparing two environment variables.
985 *
986 * @returns -1, 0, 1. See PFNRTSORTCMP.
987 * @param pvElement1 Variable 1.
988 * @param pvElement2 Variable 2.
989 * @param pvUser Ignored.
990 */
991static DECLCALLBACK(int) rtEnvSortCompare(const void *pvElement1, const void *pvElement2, void *pvUser)
992{
993 NOREF(pvUser);
994 int iDiff = strcmp((const char *)pvElement1, (const char *)pvElement2);
995 if (iDiff < 0)
996 iDiff = -1;
997 else if (iDiff > 0)
998 iDiff = 1;
999 return iDiff;
1000}
1001
1002
1003RTDECL(int) RTEnvQueryUtf16Block(RTENV hEnv, PRTUTF16 *ppwszzBlock)
1004{
1005 RTENV hClone = NIL_RTENV;
1006 PRTENVINTERNAL pIntEnv;
1007 int rc;
1008
1009 /*
1010 * Validate / simplify input.
1011 */
1012 if (hEnv == RTENV_DEFAULT)
1013 {
1014 rc = RTEnvClone(&hClone, RTENV_DEFAULT);
1015 if (RT_FAILURE(rc))
1016 return rc;
1017 pIntEnv = hClone;
1018 }
1019 else
1020 {
1021 pIntEnv = hEnv;
1022 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
1023 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
1024 rc = VINF_SUCCESS;
1025 }
1026
1027 RTENV_LOCK(pIntEnv);
1028
1029 /*
1030 * Sort it first.
1031 */
1032 RTSortApvShell((void **)pIntEnv->papszEnv, pIntEnv->cVars, rtEnvSortCompare, pIntEnv);
1033
1034 /*
1035 * Calculate the size.
1036 */
1037 size_t cwc;
1038 size_t cwcTotal = 2;
1039 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
1040 {
1041 rc = RTStrCalcUtf16LenEx(pIntEnv->papszEnv[iVar], RTSTR_MAX, &cwc);
1042 AssertRCBreak(rc);
1043 cwcTotal += cwc + 1;
1044 }
1045
1046 PRTUTF16 pwszzBlock = NULL;
1047 if (RT_SUCCESS(rc))
1048 {
1049 /*
1050 * Perform the conversion.
1051 */
1052 PRTUTF16 pwszz = pwszzBlock = (PRTUTF16)RTMemAlloc(cwcTotal * sizeof(RTUTF16));
1053 if (pwszz)
1054 {
1055 size_t cwcLeft = cwcTotal;
1056 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
1057 {
1058 rc = RTStrToUtf16Ex(pIntEnv->papszEnv[iVar], RTSTR_MAX,
1059 &pwszz, cwcTotal - (pwszz - pwszzBlock), &cwc);
1060 AssertRCBreak(rc);
1061 pwszz += cwc + 1;
1062 cwcLeft -= cwc + 1;
1063 AssertBreakStmt(cwcLeft >= 2, rc = VERR_INTERNAL_ERROR_3);
1064 }
1065 AssertStmt(cwcLeft == 2 || RT_FAILURE(rc), rc = VERR_INTERNAL_ERROR_2);
1066 if (RT_SUCCESS(rc))
1067 {
1068 pwszz[0] = '\0';
1069 pwszz[1] = '\0';
1070 }
1071 else
1072 {
1073 RTMemFree(pwszzBlock);
1074 pwszzBlock = NULL;
1075 }
1076 }
1077 else
1078 rc = VERR_NO_MEMORY;
1079 }
1080
1081 RTENV_UNLOCK(pIntEnv);
1082
1083 if (hClone != NIL_RTENV)
1084 RTEnvDestroy(hClone);
1085 if (RT_SUCCESS(rc))
1086 *ppwszzBlock = pwszzBlock;
1087 return rc;
1088}
1089RT_EXPORT_SYMBOL(RTEnvQueryUtf16Block);
1090
1091
1092RTDECL(void) RTEnvFreeUtf16Block(PRTUTF16 pwszzBlock)
1093{
1094 RTMemFree(pwszzBlock);
1095}
1096RT_EXPORT_SYMBOL(RTEnvFreeUtf16Block);
1097
1098
1099RTDECL(int) RTEnvQueryUtf8Block(RTENV hEnv, bool fSorted, char **ppszzBlock, size_t *pcbBlock)
1100{
1101 RTENV hClone = NIL_RTENV;
1102 PRTENVINTERNAL pIntEnv;
1103 int rc;
1104
1105 /*
1106 * Validate / simplify input.
1107 */
1108 if (hEnv == RTENV_DEFAULT)
1109 {
1110 rc = RTEnvClone(&hClone, RTENV_DEFAULT);
1111 if (RT_FAILURE(rc))
1112 return rc;
1113 pIntEnv = hClone;
1114 }
1115 else
1116 {
1117 pIntEnv = hEnv;
1118 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
1119 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
1120 rc = VINF_SUCCESS;
1121 }
1122
1123 RTENV_LOCK(pIntEnv);
1124
1125 /*
1126 * Sort it, if requested.
1127 */
1128 if (fSorted)
1129 RTSortApvShell((void **)pIntEnv->papszEnv, pIntEnv->cVars, rtEnvSortCompare, pIntEnv);
1130
1131 /*
1132 * Calculate the size. We add one extra terminator just to be on the safe side.
1133 */
1134 size_t cbBlock = 2;
1135 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
1136 cbBlock += strlen(pIntEnv->papszEnv[iVar]) + 1;
1137
1138 if (pcbBlock)
1139 *pcbBlock = cbBlock - 1;
1140
1141 /*
1142 * Allocate memory and copy out the variables.
1143 */
1144 char *pszzBlock;
1145 char *pszz = pszzBlock = (char *)RTMemAlloc(cbBlock);
1146 if (pszz)
1147 {
1148 size_t cbLeft = cbBlock;
1149 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
1150 {
1151 size_t cb = strlen(pIntEnv->papszEnv[iVar]) + 1;
1152 AssertBreakStmt(cb + 2 <= cbLeft, rc = VERR_INTERNAL_ERROR_3);
1153 memcpy(pszz, pIntEnv->papszEnv[iVar], cb);
1154 pszz += cb;
1155 cbLeft -= cb;
1156 }
1157 if (RT_SUCCESS(rc))
1158 {
1159 pszz[0] = '\0';
1160 pszz[1] = '\0'; /* The extra one. */
1161 }
1162 else
1163 {
1164 RTMemFree(pszzBlock);
1165 pszzBlock = NULL;
1166 }
1167 }
1168 else
1169 rc = VERR_NO_MEMORY;
1170
1171 RTENV_UNLOCK(pIntEnv);
1172
1173 if (hClone != NIL_RTENV)
1174 RTEnvDestroy(hClone);
1175 if (RT_SUCCESS(rc))
1176 *ppszzBlock = pszzBlock;
1177 return rc;
1178}
1179RT_EXPORT_SYMBOL(RTEnvQueryUtf8Block);
1180
1181
1182RTDECL(void) RTEnvFreeUtf8Block(char *pszzBlock)
1183{
1184 RTMemFree(pszzBlock);
1185}
1186RT_EXPORT_SYMBOL(RTEnvFreeUtf8Block);
1187
1188
1189RTDECL(uint32_t) RTEnvCountEx(RTENV hEnv)
1190{
1191 PRTENVINTERNAL pIntEnv = hEnv;
1192 AssertPtrReturn(pIntEnv, UINT32_MAX);
1193 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, UINT32_MAX);
1194
1195 RTENV_LOCK(pIntEnv);
1196 uint32_t cVars = (uint32_t)pIntEnv->cVars;
1197 RTENV_UNLOCK(pIntEnv);
1198
1199 return cVars;
1200}
1201RT_EXPORT_SYMBOL(RTEnvCountEx);
1202
1203
1204RTDECL(int) RTEnvGetByIndexEx(RTENV hEnv, uint32_t iVar, char *pszVar, size_t cbVar, char *pszValue, size_t cbValue)
1205{
1206 PRTENVINTERNAL pIntEnv = hEnv;
1207 AssertPtrReturn(pIntEnv, UINT32_MAX);
1208 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, UINT32_MAX);
1209 if (cbVar)
1210 AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
1211 if (cbValue)
1212 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
1213
1214 RTENV_LOCK(pIntEnv);
1215
1216 int rc;
1217 if (iVar < pIntEnv->cVars)
1218 {
1219 const char *pszSrcVar = pIntEnv->papszEnv[iVar];
1220 const char *pszSrcValue = strchr(pszSrcVar, '=');
1221 if (pszSrcValue == pszSrcVar && pIntEnv->fFirstEqual)
1222 pszSrcValue = strchr(pszSrcVar + 1, '=');
1223 bool fHasEqual = pszSrcValue != NULL;
1224 if (pszSrcValue)
1225 {
1226 pszSrcValue++;
1227 rc = VINF_SUCCESS;
1228 }
1229 else
1230 {
1231 pszSrcValue = strchr(pszSrcVar, '\0');
1232 rc = VINF_ENV_VAR_UNSET;
1233 }
1234 if (cbVar)
1235 {
1236 int rc2 = RTStrCopyEx(pszVar, cbVar, pszSrcVar, pszSrcValue - pszSrcVar - fHasEqual);
1237 if (RT_FAILURE(rc2))
1238 rc = rc2;
1239 }
1240 if (cbValue)
1241 {
1242 int rc2 = RTStrCopy(pszValue, cbValue, pszSrcValue);
1243 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1244 rc = rc2;
1245 }
1246 }
1247 else
1248 rc = VERR_ENV_VAR_NOT_FOUND;
1249
1250 RTENV_UNLOCK(pIntEnv);
1251
1252 return rc;
1253}
1254RT_EXPORT_SYMBOL(RTEnvGetByIndexEx);
1255
1256
1257RTDECL(const char *) RTEnvGetByIndexRawEx(RTENV hEnv, uint32_t iVar)
1258{
1259 PRTENVINTERNAL pIntEnv = hEnv;
1260 AssertPtrReturn(pIntEnv, NULL);
1261 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, NULL);
1262
1263 RTENV_LOCK(pIntEnv);
1264
1265 const char *pszRet;
1266 if (iVar < pIntEnv->cVars)
1267 pszRet = pIntEnv->papszEnv[iVar];
1268 else
1269 pszRet = NULL;
1270
1271 RTENV_UNLOCK(pIntEnv);
1272
1273 return pszRet;
1274}
1275RT_EXPORT_SYMBOL(RTEnvGetByIndexRawEx);
1276
1277
1278RTDECL(int) RTEnvCreateChangeRecord(PRTENV phEnv)
1279{
1280 AssertPtrReturn(phEnv, VERR_INVALID_POINTER);
1281#ifdef RTENV_ALLOW_EQUAL_FIRST_IN_VAR
1282 return rtEnvCreate(phEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, true /*fPutEnvBlock*/, true /*fFirstEqual*/);
1283#else
1284 return rtEnvCreate(phEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, true /*fPutEnvBlock*/, false /*fFirstEqual*/);
1285#endif
1286}
1287RT_EXPORT_SYMBOL(RTEnvCreateChangeRecord);
1288
1289
1290RTDECL(int) RTEnvCreateChangeRecordEx(PRTENV phEnv, uint32_t fFlags)
1291{
1292 AssertPtrReturn(phEnv, VERR_INVALID_POINTER);
1293 AssertReturn(!(fFlags & ~RTENV_CREATE_F_VALID_MASK), VERR_INVALID_FLAGS);
1294 return rtEnvCreate(phEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, true /*fPutEnvBlock*/,
1295 RT_BOOL(fFlags & RTENV_CREATE_F_ALLOW_EQUAL_FIRST_IN_VAR));
1296}
1297RT_EXPORT_SYMBOL(RTEnvCreateChangeRecord);
1298
1299
1300RTDECL(bool) RTEnvIsChangeRecord(RTENV hEnv)
1301{
1302 if (hEnv == RTENV_DEFAULT)
1303 return false;
1304
1305 PRTENVINTERNAL pIntEnv = hEnv;
1306 AssertPtrReturn(pIntEnv, false);
1307 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, false);
1308 return pIntEnv->fPutEnvBlock;
1309}
1310RT_EXPORT_SYMBOL(RTEnvIsChangeRecord);
1311
1312
1313RTDECL(int) RTEnvApplyChanges(RTENV hEnvDst, RTENV hEnvChanges)
1314{
1315 PRTENVINTERNAL pIntEnvChanges = hEnvChanges;
1316 AssertPtrReturn(pIntEnvChanges, VERR_INVALID_HANDLE);
1317 AssertReturn(pIntEnvChanges->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
1318
1319 /** @todo lock validator trouble ahead here! */
1320 RTENV_LOCK(pIntEnvChanges);
1321
1322 int rc = VINF_SUCCESS;
1323 for (uint32_t iChange = 0; iChange < pIntEnvChanges->cVars && RT_SUCCESS(rc); iChange++)
1324 rc = RTEnvPutEx(hEnvDst, pIntEnvChanges->papszEnv[iChange]);
1325
1326 RTENV_UNLOCK(pIntEnvChanges);
1327
1328 return rc;
1329}
1330RT_EXPORT_SYMBOL(RTEnvApplyChanges);
1331
Note: See TracBrowser for help on using the repository browser.

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