VirtualBox

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

Last change on this file since 95954 was 95842, checked in by vboxsync, 3 years ago

IPRT/env-generic.cpp: Refactored RTEnvCloneEx, rewriting the cloning of the default environment on windows to be more no-crt compatible and race free. Don't expose RTEnvGetExecEnvP on Windows, removing it from the stable function list. bugref:10261

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