VirtualBox

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

Last change on this file since 58591 was 57835, checked in by vboxsync, 9 years ago

RTProc,RTEnv: First pass over the windows process creation code, filling in gaps, reworking, tidying and adding todos.

  • 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 57835 2015-09-19 00:28:38Z vboxsync $ */
2/** @file
3 * IPRT - Environment, Generic.
4 */
5
6/*
7 * Copyright (C) 2006-2015 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/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, true /*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) RTEnvCloneUtf16Block(PRTENV phEnv, PCRTUTF16 pwszzBlock, uint32_t fFlags)
353{
354 AssertPtrReturn(pwszzBlock, VERR_INVALID_POINTER);
355 AssertReturn(!fFlags, VERR_INVALID_FLAGS);
356
357 /*
358 * Count the number of variables in the block.
359 */
360 uint32_t cVars = 0;
361 PCRTUTF16 pwsz = pwszzBlock;
362 while (*pwsz != '\0')
363 {
364 cVars++;
365 pwsz += RTUtf16Len(pwsz) + 1;
366 AssertReturn(cVars < _256K, VERR_OUT_OF_RANGE);
367 }
368
369 /*
370 * Create the duplicate.
371 */
372 PRTENVINTERNAL pIntEnv;
373 int rc = rtEnvCreate(&pIntEnv, cVars + 1 /* NULL */, false /*fCaseSensitive*/, false /*fPutEnvBlock*/);
374 if (RT_SUCCESS(rc))
375 {
376 pIntEnv->cVars = cVars;
377 pIntEnv->papszEnv[pIntEnv->cVars] = NULL;
378
379 size_t iDst = 0;
380 for (pwsz = pwszzBlock; *pwsz != '\0'; pwsz += RTUtf16Len(pwsz) + 1)
381 {
382 int rc2 = RTUtf16ToUtf8(pwsz, &pIntEnv->papszEnv[iDst]);
383 if (RT_SUCCESS(rc2))
384 {
385 /* Make sure it contains an '='. */
386 const char *pszEqual = strchr(pIntEnv->papszEnv[iDst], '=');
387 if (!pszEqual)
388 {
389 rc2 = RTStrAAppend(&pIntEnv->papszEnv[iDst], "=");
390 if (RT_SUCCESS(rc2))
391 pszEqual = strchr(pIntEnv->papszEnv[iDst], '=');
392
393 }
394 if (pszEqual)
395 {
396 /* Check for duplicates, keep the last version. */
397 const char *pchVar = pIntEnv->papszEnv[iDst];
398 size_t cchVarNmAndEq = pszEqual - pchVar;
399 for (size_t iDst2 = 0; iDst2 < iDst; iDst2++)
400 if (pIntEnv->pfnCompare(pIntEnv->papszEnv[iDst2], pchVar, cchVarNmAndEq) == 0)
401 {
402 RTStrFree(pIntEnv->papszEnv[iDst2]);
403 pIntEnv->papszEnv[iDst2] = pIntEnv->papszEnv[iDst];
404 pIntEnv->papszEnv[iDst] = NULL;
405 iDst--;
406 break;
407 }
408 iDst++;
409 continue;
410 }
411 iDst++;
412 }
413
414 /* failed fatally. */
415 pIntEnv->cVars = iDst;
416 RTEnvDestroy(pIntEnv);
417 return rc2;
418 }
419 Assert(iDst <= pIntEnv->cVars);
420 pIntEnv->cVars = iDst;
421
422 /* done */
423 *phEnv = pIntEnv;
424 }
425 return rc;
426}
427RT_EXPORT_SYMBOL(RTEnvCloneUtf16Block);
428
429
430
431RTDECL(int) RTEnvReset(RTENV hEnv)
432{
433 PRTENVINTERNAL pIntEnv = hEnv;
434 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
435 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
436
437 RTENV_LOCK(pIntEnv);
438
439 size_t iVar = pIntEnv->cVars;
440 pIntEnv->cVars = 0;
441 while (iVar-- > 0)
442 {
443 RTMemFree(pIntEnv->papszEnv[iVar]);
444 pIntEnv->papszEnv[iVar] = NULL;
445 }
446
447 RTENV_UNLOCK(pIntEnv);
448 return VINF_SUCCESS;
449}
450RT_EXPORT_SYMBOL(RTEnvReset);
451
452
453RTDECL(int) RTEnvPutEx(RTENV Env, const char *pszVarEqualValue)
454{
455 int rc;
456 AssertPtrReturn(pszVarEqualValue, VERR_INVALID_POINTER);
457 const char *pszEq = strchr(pszVarEqualValue, '=');
458 if (!pszEq)
459 rc = RTEnvUnsetEx(Env, pszVarEqualValue);
460 else
461 {
462 /*
463 * Make a copy of the variable name so we can terminate it
464 * properly and then pass the request on to RTEnvSetEx.
465 */
466 const char *pszValue = pszEq + 1;
467
468 size_t cchVar = pszEq - pszVarEqualValue;
469 Assert(cchVar < 1024);
470 char *pszVar = (char *)alloca(cchVar + 1);
471 memcpy(pszVar, pszVarEqualValue, cchVar);
472 pszVar[cchVar] = '\0';
473
474 rc = RTEnvSetEx(Env, pszVar, pszValue);
475 }
476 return rc;
477}
478RT_EXPORT_SYMBOL(RTEnvPutEx);
479
480
481/**
482 * Appends an already allocated string to papszEnv.
483 *
484 * @returns IPRT status code
485 * @param pIntEnv The environment block to append it to.
486 * @param pszEntry The string to add. Already duplicated, caller
487 * does error cleanup.
488 */
489static int rtEnvIntAppend(PRTENVINTERNAL pIntEnv, char *pszEntry)
490{
491 /*
492 * Do we need to resize the array?
493 */
494 int rc = VINF_SUCCESS;
495 size_t iVar = pIntEnv->cVars;
496 if (iVar + 2 > pIntEnv->cAllocated)
497 {
498 void *pvNew = RTMemRealloc(pIntEnv->papszEnv, sizeof(char *) * (pIntEnv->cAllocated + RTENV_GROW_SIZE));
499 if (!pvNew)
500 rc = VERR_NO_MEMORY;
501 else
502 {
503 pIntEnv->papszEnv = (char **)pvNew;
504 pIntEnv->cAllocated += RTENV_GROW_SIZE;
505 for (size_t iNewVar = pIntEnv->cVars; iNewVar < pIntEnv->cAllocated; iNewVar++)
506 pIntEnv->papszEnv[iNewVar] = NULL;
507 }
508 }
509 if (RT_SUCCESS(rc))
510 {
511 /*
512 * Append it.
513 */
514 pIntEnv->papszEnv[iVar] = pszEntry;
515 pIntEnv->papszEnv[iVar + 1] = NULL; /* this isn't really necessary, but doesn't hurt. */
516 pIntEnv->cVars = iVar + 1;
517 }
518 return rc;
519}
520
521
522RTDECL(int) RTEnvSetEx(RTENV Env, const char *pszVar, const char *pszValue)
523{
524 AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
525 AssertReturn(*pszVar, VERR_INVALID_PARAMETER);
526 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
527 AssertReturn(strchr(pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
528
529 int rc;
530 if (Env == RTENV_DEFAULT)
531 {
532#ifdef RT_OS_WINDOWS
533 rc = RTEnvSetUtf8(pszVar, pszValue);
534#else
535 /*
536 * Since RTEnvPut isn't UTF-8 clean and actually expects the strings
537 * to be in the current code page (codeset), we'll do the necessary
538 * conversions here.
539 */
540 char *pszVarOtherCP;
541 rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
542 if (RT_SUCCESS(rc))
543 {
544 char *pszValueOtherCP;
545 rc = RTStrUtf8ToCurrentCP(&pszValueOtherCP, pszValue);
546 if (RT_SUCCESS(rc))
547 {
548 rc = RTEnvSet(pszVarOtherCP, pszValueOtherCP);
549 RTStrFree(pszValueOtherCP);
550 }
551 RTStrFree(pszVarOtherCP);
552 }
553#endif
554 }
555 else
556 {
557 PRTENVINTERNAL pIntEnv = Env;
558 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
559 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
560
561 /*
562 * Create the variable string.
563 */
564 const size_t cchVar = strlen(pszVar);
565 const size_t cchValue = strlen(pszValue);
566 char *pszEntry = (char *)RTMemAlloc(cchVar + cchValue + 2);
567 if (pszEntry)
568 {
569 memcpy(pszEntry, pszVar, cchVar);
570 pszEntry[cchVar] = '=';
571 memcpy(&pszEntry[cchVar + 1], pszValue, cchValue + 1);
572
573 RTENV_LOCK(pIntEnv);
574
575 /*
576 * Find the location of the variable. (iVar = cVars if new)
577 */
578 rc = VINF_SUCCESS;
579 size_t iVar;
580 for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
581 if ( !pIntEnv->pfnCompare(pIntEnv->papszEnv[iVar], pszVar, cchVar)
582 && ( pIntEnv->papszEnv[iVar][cchVar] == '='
583 || pIntEnv->papszEnv[iVar][cchVar] == '\0') )
584 break;
585 if (iVar < pIntEnv->cVars)
586 {
587 /*
588 * Replace the current entry. Simple.
589 */
590 RTMemFree(pIntEnv->papszEnv[iVar]);
591 pIntEnv->papszEnv[iVar] = pszEntry;
592 }
593 else
594 {
595 /*
596 * New variable, append it.
597 */
598 Assert(pIntEnv->cVars == iVar);
599 rc = rtEnvIntAppend(pIntEnv, pszEntry);
600 }
601
602 RTENV_UNLOCK(pIntEnv);
603
604 if (RT_FAILURE(rc))
605 RTMemFree(pszEntry);
606 }
607 else
608 rc = VERR_NO_MEMORY;
609 }
610 return rc;
611}
612RT_EXPORT_SYMBOL(RTEnvSetEx);
613
614
615RTDECL(int) RTEnvUnsetEx(RTENV Env, const char *pszVar)
616{
617 AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
618 AssertReturn(*pszVar, VERR_INVALID_PARAMETER);
619 AssertReturn(strchr(pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
620
621 int rc;
622 if (Env == RTENV_DEFAULT)
623 {
624#ifdef RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API
625 rc = RTEnvUnsetUtf8(pszVar);
626#else
627 /*
628 * Since RTEnvUnset isn't UTF-8 clean and actually expects the strings
629 * to be in the current code page (codeset), we'll do the necessary
630 * conversions here.
631 */
632 char *pszVarOtherCP;
633 rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
634 if (RT_SUCCESS(rc))
635 {
636 rc = RTEnvUnset(pszVarOtherCP);
637 RTStrFree(pszVarOtherCP);
638 }
639#endif
640 }
641 else
642 {
643 PRTENVINTERNAL pIntEnv = Env;
644 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
645 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
646
647 RTENV_LOCK(pIntEnv);
648
649 /*
650 * Remove all variable by the given name.
651 */
652 rc = VINF_ENV_VAR_NOT_FOUND;
653 const size_t cchVar = strlen(pszVar);
654 size_t iVar;
655 for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
656 if ( !pIntEnv->pfnCompare(pIntEnv->papszEnv[iVar], pszVar, cchVar)
657 && ( pIntEnv->papszEnv[iVar][cchVar] == '='
658 || pIntEnv->papszEnv[iVar][cchVar] == '\0') )
659 {
660 if (!pIntEnv->fPutEnvBlock)
661 {
662 RTMemFree(pIntEnv->papszEnv[iVar]);
663 pIntEnv->cVars--;
664 if (pIntEnv->cVars > 0)
665 pIntEnv->papszEnv[iVar] = pIntEnv->papszEnv[pIntEnv->cVars];
666 pIntEnv->papszEnv[pIntEnv->cVars] = NULL;
667 }
668 else
669 {
670 /* Record this unset by keeping the variable without any equal sign. */
671 pIntEnv->papszEnv[iVar][cchVar] = '\0';
672 }
673 rc = VINF_SUCCESS;
674 /* no break, there could be more. */
675 }
676
677 /*
678 * If this is a change record, we may need to add it.
679 */
680 if (rc == VINF_ENV_VAR_NOT_FOUND && pIntEnv->fPutEnvBlock)
681 {
682 char *pszEntry = (char *)RTMemDup(pszVar, cchVar + 1);
683 if (pszEntry)
684 {
685 rc = rtEnvIntAppend(pIntEnv, pszEntry);
686 if (RT_SUCCESS(rc))
687 rc = VINF_ENV_VAR_NOT_FOUND;
688 else
689 RTMemFree(pszEntry);
690 }
691 else
692 rc = VERR_NO_MEMORY;
693 }
694
695 RTENV_UNLOCK(pIntEnv);
696 }
697 return rc;
698
699}
700RT_EXPORT_SYMBOL(RTEnvUnsetEx);
701
702
703RTDECL(int) RTEnvGetEx(RTENV Env, const char *pszVar, char *pszValue, size_t cbValue, size_t *pcchActual)
704{
705 AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
706 AssertPtrNullReturn(pszValue, VERR_INVALID_POINTER);
707 AssertPtrNullReturn(pcchActual, VERR_INVALID_POINTER);
708 AssertReturn(pcchActual || (pszValue && cbValue), VERR_INVALID_PARAMETER);
709 AssertReturn(strchr(pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
710
711 if (pcchActual)
712 *pcchActual = 0;
713 int rc;
714 if (Env == RTENV_DEFAULT)
715 {
716#ifdef RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API
717 rc = RTEnvGetUtf8(pszVar, pszValue, cbValue, pcchActual);
718#else
719 /*
720 * Since RTEnvGet isn't UTF-8 clean and actually expects the strings
721 * to be in the current code page (codeset), we'll do the necessary
722 * conversions here.
723 */
724 char *pszVarOtherCP;
725 rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
726 if (RT_SUCCESS(rc))
727 {
728 const char *pszValueOtherCP = RTEnvGet(pszVarOtherCP);
729 RTStrFree(pszVarOtherCP);
730 if (pszValueOtherCP)
731 {
732 char *pszValueUtf8;
733 rc = RTStrCurrentCPToUtf8(&pszValueUtf8, pszValueOtherCP);
734 if (RT_SUCCESS(rc))
735 {
736 rc = VINF_SUCCESS;
737 size_t cch = strlen(pszValueUtf8);
738 if (pcchActual)
739 *pcchActual = cch;
740 if (pszValue && cbValue)
741 {
742 if (cch < cbValue)
743 memcpy(pszValue, pszValueUtf8, cch + 1);
744 else
745 rc = VERR_BUFFER_OVERFLOW;
746 }
747 RTStrFree(pszValueUtf8);
748 }
749 }
750 else
751 rc = VERR_ENV_VAR_NOT_FOUND;
752 }
753#endif
754 }
755 else
756 {
757 PRTENVINTERNAL pIntEnv = Env;
758 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
759 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
760
761 RTENV_LOCK(pIntEnv);
762
763 /*
764 * Locate the first variable and return it to the caller.
765 */
766 rc = VERR_ENV_VAR_NOT_FOUND;
767 const size_t cchVar = strlen(pszVar);
768 size_t iVar;
769 for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
770 if (!pIntEnv->pfnCompare(pIntEnv->papszEnv[iVar], pszVar, cchVar))
771 {
772 if (pIntEnv->papszEnv[iVar][cchVar] == '=')
773 {
774 rc = VINF_SUCCESS;
775 const char *pszValueOrg = pIntEnv->papszEnv[iVar] + cchVar + 1;
776 size_t cch = strlen(pszValueOrg);
777 if (pcchActual)
778 *pcchActual = cch;
779 if (pszValue && cbValue)
780 {
781 if (cch < cbValue)
782 memcpy(pszValue, pszValueOrg, cch + 1);
783 else
784 rc = VERR_BUFFER_OVERFLOW;
785 }
786 break;
787 }
788 if (pIntEnv->papszEnv[iVar][cchVar] == '\0')
789 {
790 Assert(pIntEnv->fPutEnvBlock);
791 rc = VERR_ENV_VAR_UNSET;
792 break;
793 }
794 }
795
796 RTENV_UNLOCK(pIntEnv);
797 }
798 return rc;
799}
800RT_EXPORT_SYMBOL(RTEnvGetEx);
801
802
803RTDECL(bool) RTEnvExistEx(RTENV Env, const char *pszVar)
804{
805 AssertPtrReturn(pszVar, false);
806
807 bool fExists = false;
808 if (Env == RTENV_DEFAULT)
809 {
810#ifdef RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API
811 fExists = RTEnvExistsUtf8(pszVar);
812#else
813 /*
814 * Since RTEnvExist isn't UTF-8 clean and actually expects the strings
815 * to be in the current code page (codeset), we'll do the necessary
816 * conversions here.
817 */
818 char *pszVarOtherCP;
819 int rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
820 if (RT_SUCCESS(rc))
821 {
822 fExists = RTEnvExist(pszVarOtherCP);
823 RTStrFree(pszVarOtherCP);
824 }
825#endif
826 }
827 else
828 {
829 PRTENVINTERNAL pIntEnv = Env;
830 AssertPtrReturn(pIntEnv, false);
831 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, false);
832
833 RTENV_LOCK(pIntEnv);
834
835 /*
836 * Simple search.
837 */
838 const size_t cchVar = strlen(pszVar);
839 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
840 if (!pIntEnv->pfnCompare(pIntEnv->papszEnv[iVar], pszVar, cchVar))
841 {
842 if (pIntEnv->papszEnv[iVar][cchVar] == '=')
843 {
844 fExists = true;
845 break;
846 }
847 if (pIntEnv->papszEnv[iVar][cchVar] == '\0')
848 break;
849 }
850
851 RTENV_UNLOCK(pIntEnv);
852 }
853 return fExists;
854}
855RT_EXPORT_SYMBOL(RTEnvExistEx);
856
857
858RTDECL(char const * const *) RTEnvGetExecEnvP(RTENV Env)
859{
860 const char * const *papszRet;
861 if (Env == RTENV_DEFAULT)
862 {
863 /** @todo fix this API it's fundamentally wrong! */
864 papszRet = rtEnvDefault();
865 if (!papszRet)
866 {
867 static const char * const s_papszDummy[2] = { NULL, NULL };
868 papszRet = &s_papszDummy[0];
869 }
870 }
871 else
872 {
873 PRTENVINTERNAL pIntEnv = Env;
874 AssertPtrReturn(pIntEnv, NULL);
875 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, NULL);
876
877 RTENV_LOCK(pIntEnv);
878
879 /*
880 * Free any old envp.
881 */
882 if (pIntEnv->papszEnvOtherCP)
883 {
884 for (size_t iVar = 0; pIntEnv->papszEnvOtherCP[iVar]; iVar++)
885 {
886 RTStrFree(pIntEnv->papszEnvOtherCP[iVar]);
887 pIntEnv->papszEnvOtherCP[iVar] = NULL;
888 }
889 RTMemFree(pIntEnv->papszEnvOtherCP);
890 pIntEnv->papszEnvOtherCP = NULL;
891 }
892
893 /*
894 * Construct a new envp with the strings in the process code set.
895 */
896 char **papsz;
897 papszRet = pIntEnv->papszEnvOtherCP = papsz = (char **)RTMemAlloc(sizeof(char *) * (pIntEnv->cVars + 1));
898 if (papsz)
899 {
900 papsz[pIntEnv->cVars] = NULL;
901 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
902 {
903 int rc = RTStrUtf8ToCurrentCP(&papsz[iVar], pIntEnv->papszEnv[iVar]);
904 if (RT_FAILURE(rc))
905 {
906 /* RTEnvDestroy / we cleans up later. */
907 papsz[iVar] = NULL;
908 AssertRC(rc);
909 papszRet = NULL;
910 break;
911 }
912 }
913 }
914
915 RTENV_UNLOCK(pIntEnv);
916 }
917 return papszRet;
918}
919RT_EXPORT_SYMBOL(RTEnvGetExecEnvP);
920
921
922/**
923 * RTSort callback for comparing two environment variables.
924 *
925 * @returns -1, 0, 1. See PFNRTSORTCMP.
926 * @param pvElement1 Variable 1.
927 * @param pvElement2 Variable 2.
928 * @param pvUser Ignored.
929 */
930static DECLCALLBACK(int) rtEnvSortCompare(const void *pvElement1, const void *pvElement2, void *pvUser)
931{
932 NOREF(pvUser);
933 int iDiff = strcmp((const char *)pvElement1, (const char *)pvElement2);
934 if (iDiff < 0)
935 iDiff = -1;
936 else if (iDiff > 0)
937 iDiff = 1;
938 return iDiff;
939}
940
941
942RTDECL(int) RTEnvQueryUtf16Block(RTENV hEnv, PRTUTF16 *ppwszzBlock)
943{
944 RTENV hClone = NIL_RTENV;
945 PRTENVINTERNAL pIntEnv;
946 int rc;
947
948 /*
949 * Validate / simplify input.
950 */
951 if (hEnv == RTENV_DEFAULT)
952 {
953 rc = RTEnvClone(&hClone, RTENV_DEFAULT);
954 if (RT_FAILURE(rc))
955 return rc;
956 pIntEnv = hClone;
957 }
958 else
959 {
960 pIntEnv = hEnv;
961 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
962 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
963 rc = VINF_SUCCESS;
964 }
965
966 RTENV_LOCK(pIntEnv);
967
968 /*
969 * Sort it first.
970 */
971 RTSortApvShell((void **)pIntEnv->papszEnv, pIntEnv->cVars, rtEnvSortCompare, pIntEnv);
972
973 /*
974 * Calculate the size.
975 */
976 size_t cwc;
977 size_t cwcTotal = 2;
978 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
979 {
980 rc = RTStrCalcUtf16LenEx(pIntEnv->papszEnv[iVar], RTSTR_MAX, &cwc);
981 AssertRCBreak(rc);
982 cwcTotal += cwc + 1;
983 }
984
985 PRTUTF16 pwszzBlock = NULL;
986 if (RT_SUCCESS(rc))
987 {
988 /*
989 * Perform the conversion.
990 */
991 PRTUTF16 pwszz = pwszzBlock = (PRTUTF16)RTMemAlloc(cwcTotal * sizeof(RTUTF16));
992 if (pwszz)
993 {
994 size_t cwcLeft = cwcTotal;
995 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
996 {
997 rc = RTStrToUtf16Ex(pIntEnv->papszEnv[iVar], RTSTR_MAX,
998 &pwszz, cwcTotal - (pwszz - pwszzBlock), &cwc);
999 AssertRCBreak(rc);
1000 pwszz += cwc + 1;
1001 cwcLeft -= cwc + 1;
1002 AssertBreakStmt(cwcLeft >= 2, rc = VERR_INTERNAL_ERROR_3);
1003 }
1004 AssertStmt(cwcLeft == 2 || RT_FAILURE(rc), rc = VERR_INTERNAL_ERROR_2);
1005 if (RT_SUCCESS(rc))
1006 {
1007 pwszz[0] = '\0';
1008 pwszz[1] = '\0';
1009 }
1010 else
1011 {
1012 RTMemFree(pwszzBlock);
1013 pwszzBlock = NULL;
1014 }
1015 }
1016 else
1017 rc = VERR_NO_MEMORY;
1018 }
1019
1020 RTENV_UNLOCK(pIntEnv);
1021
1022 if (hClone != NIL_RTENV)
1023 RTEnvDestroy(hClone);
1024 if (RT_SUCCESS(rc))
1025 *ppwszzBlock = pwszzBlock;
1026 return rc;
1027}
1028RT_EXPORT_SYMBOL(RTEnvQueryUtf16Block);
1029
1030
1031RTDECL(void) RTEnvFreeUtf16Block(PRTUTF16 pwszzBlock)
1032{
1033 RTMemFree(pwszzBlock);
1034}
1035RT_EXPORT_SYMBOL(RTEnvFreeUtf16Block);
1036
1037
1038RTDECL(int) RTEnvQueryUtf8Block(RTENV hEnv, bool fSorted, char **ppszzBlock, size_t *pcbBlock)
1039{
1040 RTENV hClone = NIL_RTENV;
1041 PRTENVINTERNAL pIntEnv;
1042 int rc;
1043
1044 /*
1045 * Validate / simplify input.
1046 */
1047 if (hEnv == RTENV_DEFAULT)
1048 {
1049 rc = RTEnvClone(&hClone, RTENV_DEFAULT);
1050 if (RT_FAILURE(rc))
1051 return rc;
1052 pIntEnv = hClone;
1053 }
1054 else
1055 {
1056 pIntEnv = hEnv;
1057 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
1058 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
1059 rc = VINF_SUCCESS;
1060 }
1061
1062 RTENV_LOCK(pIntEnv);
1063
1064 /*
1065 * Sort it, if requested.
1066 */
1067 if (fSorted)
1068 RTSortApvShell((void **)pIntEnv->papszEnv, pIntEnv->cVars, rtEnvSortCompare, pIntEnv);
1069
1070 /*
1071 * Calculate the size. We add one extra terminator just to be on the safe side.
1072 */
1073 size_t cbBlock = 2;
1074 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
1075 cbBlock += strlen(pIntEnv->papszEnv[iVar]) + 1;
1076
1077 if (pcbBlock)
1078 *pcbBlock = cbBlock - 1;
1079
1080 /*
1081 * Allocate memory and copy out the variables.
1082 */
1083 char *pszzBlock;
1084 char *pszz = pszzBlock = (char *)RTMemAlloc(cbBlock);
1085 if (pszz)
1086 {
1087 size_t cbLeft = cbBlock;
1088 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
1089 {
1090 size_t cb = strlen(pIntEnv->papszEnv[iVar]) + 1;
1091 AssertBreakStmt(cb + 2 <= cbLeft, rc = VERR_INTERNAL_ERROR_3);
1092 memcpy(pszz, pIntEnv->papszEnv[iVar], cb);
1093 pszz += cb;
1094 cbLeft -= cb;
1095 }
1096 if (RT_SUCCESS(rc))
1097 {
1098 pszz[0] = '\0';
1099 pszz[1] = '\0'; /* The extra one. */
1100 }
1101 else
1102 {
1103 RTMemFree(pszzBlock);
1104 pszzBlock = NULL;
1105 }
1106 }
1107 else
1108 rc = VERR_NO_MEMORY;
1109
1110 RTENV_UNLOCK(pIntEnv);
1111
1112 if (hClone != NIL_RTENV)
1113 RTEnvDestroy(hClone);
1114 if (RT_SUCCESS(rc))
1115 *ppszzBlock = pszzBlock;
1116 return rc;
1117}
1118RT_EXPORT_SYMBOL(RTEnvQueryUtf8Block);
1119
1120
1121RTDECL(void) RTEnvFreeUtf8Block(char *pszzBlock)
1122{
1123 RTMemFree(pszzBlock);
1124}
1125RT_EXPORT_SYMBOL(RTEnvFreeUtf8Block);
1126
1127
1128RTDECL(uint32_t) RTEnvCountEx(RTENV hEnv)
1129{
1130 PRTENVINTERNAL pIntEnv = hEnv;
1131 AssertPtrReturn(pIntEnv, UINT32_MAX);
1132 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, UINT32_MAX);
1133
1134 RTENV_LOCK(pIntEnv);
1135 uint32_t cVars = (uint32_t)pIntEnv->cVars;
1136 RTENV_UNLOCK(pIntEnv);
1137
1138 return cVars;
1139}
1140RT_EXPORT_SYMBOL(RTEnvCountEx);
1141
1142
1143RTDECL(int) RTEnvGetByIndexEx(RTENV hEnv, uint32_t iVar, char *pszVar, size_t cbVar, char *pszValue, size_t cbValue)
1144{
1145 PRTENVINTERNAL pIntEnv = hEnv;
1146 AssertPtrReturn(pIntEnv, UINT32_MAX);
1147 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, UINT32_MAX);
1148 if (cbVar)
1149 AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
1150 if (cbValue)
1151 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
1152
1153 RTENV_LOCK(pIntEnv);
1154
1155 int rc;
1156 if (iVar < pIntEnv->cVars)
1157 {
1158 const char *pszSrcVar = pIntEnv->papszEnv[iVar];
1159 const char *pszSrcValue = strchr(pszSrcVar, '=');
1160 bool fHasEqual = pszSrcValue != NULL;
1161 if (pszSrcValue)
1162 {
1163 pszSrcValue++;
1164 rc = VINF_SUCCESS;
1165 }
1166 else
1167 {
1168 pszSrcValue = strchr(pszSrcVar, '\0');
1169 rc = VINF_ENV_VAR_UNSET;
1170 }
1171 if (cbVar)
1172 {
1173 int rc2 = RTStrCopyEx(pszVar, cbVar, pszSrcVar, pszSrcValue - pszSrcVar - fHasEqual);
1174 if (RT_FAILURE(rc2))
1175 rc = rc2;
1176 }
1177 if (cbValue)
1178 {
1179 int rc2 = RTStrCopy(pszValue, cbValue, pszSrcValue);
1180 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1181 rc = rc2;
1182 }
1183 }
1184 else
1185 rc = VERR_ENV_VAR_NOT_FOUND;
1186
1187 RTENV_UNLOCK(pIntEnv);
1188
1189 return rc;
1190}
1191RT_EXPORT_SYMBOL(RTEnvGetByIndexEx);
1192
1193
1194RTDECL(const char *) RTEnvGetByIndexRawEx(RTENV hEnv, uint32_t iVar)
1195{
1196 PRTENVINTERNAL pIntEnv = hEnv;
1197 AssertPtrReturn(pIntEnv, NULL);
1198 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, NULL);
1199
1200 RTENV_LOCK(pIntEnv);
1201
1202 const char *pszRet;
1203 if (iVar < pIntEnv->cVars)
1204 pszRet = pIntEnv->papszEnv[iVar];
1205 else
1206 pszRet = NULL;
1207
1208 RTENV_UNLOCK(pIntEnv);
1209
1210 return pszRet;
1211}
1212RT_EXPORT_SYMBOL(RTEnvGetByIndexRawEx);
1213
1214
1215RTDECL(int) RTEnvCreateChangeRecord(PRTENV phEnv)
1216{
1217 AssertPtrReturn(phEnv, VERR_INVALID_POINTER);
1218 return rtEnvCreate(phEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, true /*fPutEnvBlock*/);
1219}
1220RT_EXPORT_SYMBOL(RTEnvCreateChangeRecord);
1221
1222
1223RTDECL(bool) RTEnvIsChangeRecord(RTENV hEnv)
1224{
1225 if (hEnv == RTENV_DEFAULT)
1226 return false;
1227
1228 PRTENVINTERNAL pIntEnv = hEnv;
1229 AssertPtrReturn(pIntEnv, false);
1230 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, false);
1231 return pIntEnv->fPutEnvBlock;
1232}
1233RT_EXPORT_SYMBOL(RTEnvIsChangeRecord);
1234
1235
1236RTDECL(int) RTEnvApplyChanges(RTENV hEnvDst, RTENV hEnvChanges)
1237{
1238 PRTENVINTERNAL pIntEnvChanges = hEnvChanges;
1239 AssertPtrReturn(pIntEnvChanges, VERR_INVALID_HANDLE);
1240 AssertReturn(pIntEnvChanges->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
1241
1242 /** @todo lock validator trouble ahead here! */
1243 RTENV_LOCK(pIntEnvChanges);
1244
1245 int rc = VINF_SUCCESS;
1246 for (uint32_t iChange = 0; iChange < pIntEnvChanges->cVars && RT_SUCCESS(rc); iChange++)
1247 rc = RTEnvPutEx(hEnvDst, pIntEnvChanges->papszEnv[iChange]);
1248
1249 RTENV_UNLOCK(pIntEnvChanges);
1250
1251 return rc;
1252}
1253RT_EXPORT_SYMBOL(RTEnvApplyChanges);
1254
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