VirtualBox

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

Last change on this file since 55585 was 55585, checked in by vboxsync, 10 years ago

env-generic.cpp: build fix.

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

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