VirtualBox

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

Last change on this file since 96763 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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