VirtualBox

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

Last change on this file since 80764 was 80764, checked in by vboxsync, 5 years ago

IPRT: Optimized RTEnvPutEx. bugref:9341

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