VirtualBox

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

Last change on this file since 4475 was 4475, checked in by vboxsync, 18 years ago

Some adjustments to RTEnv and RTProcCreate. Should work on darwin now.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 19.0 KB
Line 
1/* $Id: env-generic.cpp 4475 2007-09-01 01:21:19Z vboxsync $ */
2/** @file
3 * innotek Portable Runtime - Environment, Generic.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include <iprt/env.h>
23#include <iprt/assert.h>
24#include <iprt/alloc.h>
25#include <iprt/alloca.h>
26#include <iprt/string.h>
27#include <iprt/err.h>
28#include "internal/magics.h"
29
30#include <stdlib.h>
31#if !defined(RT_OS_WINDOWS)
32# include <unistd.h>
33#endif
34#if defined(RT_OS_SOLARIS)
35__BEGIN_DECLS
36extern char **environ;
37__END_DECLS
38#endif
39
40/*******************************************************************************
41* Defined Constants And Macros *
42*******************************************************************************/
43/** Macro that unlocks the specified environment block. */
44#define RTENV_LOCK(pEnvInt) do { } while (0)
45/** Macro that unlocks the specified environment block. */
46#define RTENV_UNLOCK(pEnvInt) do { } while (0)
47
48
49/*******************************************************************************
50* Structures and Typedefs *
51*******************************************************************************/
52/**
53 * The internal representation of a (non-default) environment.
54 */
55typedef struct RTENVINTERNAL
56{
57 /** Magic value . */
58 uint32_t u32Magic;
59 /** Number of variables in the array.
60 * This does not include the terminating NULL entry. */
61 size_t cVars;
62 /** Capacity (allocated size) of the array.
63 * This includes space for the terminating NULL element (for compatibility
64 * with the C library), so that c <= cCapacity - 1. */
65 size_t cAllocated;
66 /** Array of environment variables. */
67 char **papszEnv;
68 /** Array of environment variables in the process CP.
69 * This get (re-)constructed when RTEnvGetExecEnvP method is called. */
70 char **papszEnvOtherCP;
71} RTENVINTERNAL, *PRTENVINTERNAL;
72
73/** The allocation granularity of the RTENVINTERNAL::papszEnv memory. */
74#define RTENV_GROW_SIZE 16
75
76
77/**
78 * Internal worker that resolves the pointer to the default
79 * process environment. (environ)
80 *
81 * @returns Pointer to the default environment.
82 * This may be NULL.
83 */
84static const char * const *rtEnvDefault(void)
85{
86#ifdef RT_OS_DARWIN
87 return *(_NSGetEnviron());
88#elif defined(RT_OS_L4)
89 /* So far, our L4 libraries do not include environment support. */
90 return NULL;
91#else
92 return environ;
93#endif
94}
95
96
97/**
98 * Internal worker that creates an environment handle with a specified capacity.
99 *
100 * @returns IPRT status code.
101 * @param ppIntEnv Where to store the result.
102 * @param cAllocated The initial array size.
103 */
104static int rtEnvCreate(PRTENVINTERNAL *ppIntEnv, size_t cAllocated)
105{
106 /*
107 * Allocate environment handle.
108 */
109 PRTENVINTERNAL pIntEnv = (PRTENVINTERNAL)RTMemAlloc(sizeof(*pIntEnv));
110 if (pIntEnv)
111 {
112 /*
113 * Pre-allocate the variable array.
114 */
115 pIntEnv->u32Magic = RTENV_MAGIC;
116 pIntEnv->papszEnvOtherCP = NULL;
117 pIntEnv->cVars = 0;
118 pIntEnv->cAllocated = RT_ALIGN_Z(RT_MAX(cAllocated, RTENV_GROW_SIZE), RTENV_GROW_SIZE);
119 pIntEnv->papszEnv = (char **)RTMemAllocZ(sizeof(pIntEnv->papszEnv[0]) * pIntEnv->cAllocated);
120 if (pIntEnv->papszEnv)
121 {
122 *ppIntEnv = pIntEnv;
123 return VINF_SUCCESS;
124 }
125
126 RTMemFree(pIntEnv);
127 }
128
129 return VERR_NO_MEMORY;
130}
131
132
133RTDECL(int) RTEnvCreate(PRTENV pEnv)
134{
135 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
136 return rtEnvCreate(pEnv, RTENV_GROW_SIZE);
137}
138
139
140RTDECL(int) RTEnvDestroy(RTENV Env)
141{
142 /*
143 * Ignore NIL_RTENV and validate input.
144 */
145 if ( Env == NIL_RTENV
146 || Env == RTENV_DEFAULT)
147 return VINF_SUCCESS;
148
149 PRTENVINTERNAL pIntEnv = Env;
150 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
151 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
152
153 /*
154 * Do the cleanup.
155 */
156 RTENV_LOCK(pIntEnv);
157 pIntEnv->u32Magic++;
158 size_t iVar = pIntEnv->cVars;
159 while (iVar-- > 0)
160 RTStrFree(pIntEnv->papszEnv[iVar]);
161 RTMemFree(pIntEnv->papszEnv);
162 pIntEnv->papszEnv = NULL;
163
164 if (pIntEnv->papszEnvOtherCP)
165 {
166 for (iVar = 0; pIntEnv->papszEnvOtherCP[iVar]; iVar++)
167 {
168 RTStrFree(pIntEnv->papszEnvOtherCP[iVar]);
169 pIntEnv->papszEnvOtherCP[iVar] = NULL;
170 }
171 RTMemFree(pIntEnv->papszEnvOtherCP);
172 pIntEnv->papszEnvOtherCP = NULL;
173 }
174
175 RTENV_UNLOCK(pIntEnv);
176 /*RTCritSectDelete(&pIntEnv->CritSect) */
177 RTMemFree(pIntEnv);
178
179 return VINF_SUCCESS;
180}
181
182
183RTDECL(int) RTEnvClone(PRTENV pEnv, RTENV EnvToClone)
184{
185 /*
186 * Validate input and figure out how many variable to clone and where to get them.
187 */
188 size_t cVars;
189 const char * const *papszEnv;
190 PRTENVINTERNAL pIntEnvToClone;
191 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
192 if (EnvToClone == RTENV_DEFAULT)
193 {
194 pIntEnvToClone = NULL;
195 papszEnv = rtEnvDefault();
196 cVars = 0;
197 if (papszEnv)
198 while (papszEnv[cVars])
199 cVars++;
200 }
201 else
202 {
203 pIntEnvToClone = EnvToClone;
204 AssertPtrReturn(pIntEnvToClone, VERR_INVALID_HANDLE);
205 AssertReturn(pIntEnvToClone->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
206 RTENV_LOCK(pIntEnvToClone);
207
208 papszEnv = pIntEnvToClone->papszEnv;
209 cVars = pIntEnvToClone->cVars;
210 }
211
212 /*
213 * Create the duplicate.
214 */
215 PRTENVINTERNAL pIntEnv;
216 int rc = rtEnvCreate(&pIntEnv, cVars + 1 /* NULL */);
217 if (RT_SUCCESS(rc))
218 {
219 pIntEnv->cVars = cVars;
220 pIntEnv->papszEnv[pIntEnv->cVars] = NULL;
221 if (EnvToClone == RTENV_DEFAULT)
222 {
223 /* ASSUMES the default environment is in the current codepage. */
224 for (size_t iVar = 0; iVar < cVars; iVar++)
225 {
226 int rc = RTStrCurrentCPToUtf8(&pIntEnv->papszEnv[iVar], papszEnv[iVar]);
227 if (RT_FAILURE(rc))
228 {
229 pIntEnv->cVars = iVar;
230 RTEnvDestroy(pIntEnv);
231 return rc;
232 }
233 }
234 }
235 else
236 {
237 for (size_t iVar = 0; iVar < cVars; iVar++)
238 {
239 char *pszVar = RTStrDup(papszEnv[iVar]);
240 if (RT_UNLIKELY(!pszVar))
241 {
242 RTENV_UNLOCK(pIntEnvToClone);
243
244 pIntEnv->cVars = iVar;
245 RTEnvDestroy(pIntEnv);
246 return rc;
247 }
248 pIntEnv->papszEnv[iVar] = pszVar;
249 }
250 }
251
252 /* done */
253 *pEnv = pIntEnv;
254 }
255
256 if (pIntEnvToClone)
257 RTENV_UNLOCK(pIntEnvToClone);
258 return rc;
259}
260
261
262RTDECL(int) RTEnvPutEx(RTENV Env, const char *pszVarEqualValue)
263{
264 int rc;
265 AssertPtrReturn(pszVarEqualValue, VERR_INVALID_POINTER);
266 const char *pszEq = strchr(pszVarEqualValue, '=');
267 if (!pszEq)
268 rc = RTEnvUnsetEx(Env, pszVarEqualValue);
269 else
270 {
271 /*
272 * Make a copy of the variable name so we can terminate it
273 * properly and then pass the request on to RTEnvSetEx.
274 */
275 const char *pszValue = pszEq + 1;
276
277 size_t cchVar = pszEq - pszVarEqualValue;
278 Assert(cchVar < 1024);
279 char *pszVar = (char *)alloca(cchVar + 1);
280 memcpy(pszVar, pszVarEqualValue, cchVar);
281 pszVar[cchVar] = '\0';
282
283 rc = RTEnvSetEx(Env, pszVar, pszValue);
284 }
285 return rc;
286}
287
288
289RTDECL(int) RTEnvSetEx(RTENV Env, const char *pszVar, const char *pszValue)
290{
291 AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
292 AssertPtrReturn(*pszVar, VERR_INVALID_PARAMETER);
293 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
294
295 int rc;
296 if (Env == RTENV_DEFAULT)
297 {
298 /*
299 * Since RTEnvPut isn't UTF-8 clean and actually expects the strings
300 * to be in the current code page (codeset), we'll do the necessary
301 * conversions here.
302 */
303 char *pszVarOtherCP;
304 rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
305 if (RT_SUCCESS(rc))
306 {
307 char *pszValueOtherCP;
308 rc = RTStrUtf8ToCurrentCP(&pszValueOtherCP, pszValue);
309 if (RT_SUCCESS(rc))
310 {
311 rc = RTEnvSet(pszVarOtherCP, pszValueOtherCP);
312 RTStrFree(pszValueOtherCP);
313 }
314 RTStrFree(pszVarOtherCP);
315 }
316 }
317 else
318 {
319 PRTENVINTERNAL pIntEnv = Env;
320 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
321 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
322
323 /*
324 * Create the variable string.
325 */
326 const size_t cchVar = strlen(pszVar);
327 const size_t cchValue = strlen(pszValue);
328 char *pszEntry = (char *)RTMemAlloc(cchVar + cchValue + 2);
329 if (pszEntry)
330 {
331 memcpy(pszEntry, pszVar, cchVar);
332 pszEntry[cchVar] = '=';
333 memcpy(&pszEntry[cchVar + 1], pszValue, cchValue + 1);
334
335 RTENV_LOCK(pIntEnv);
336
337 /*
338 * Find the location of the variable. (iVar = cVars if new)
339 */
340 rc = VINF_SUCCESS;
341 size_t iVar;
342 for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
343 if ( !strncmp(pIntEnv->papszEnv[iVar], pszVar, cchVar)
344 && pIntEnv->papszEnv[iVar][cchVar] == '=')
345 break;
346 if (iVar < pIntEnv->cVars)
347 {
348 /*
349 * Replace the current entry. Simple.
350 */
351 RTMemFree(pIntEnv->papszEnv[iVar]);
352 pIntEnv->papszEnv[iVar] = pszEntry;
353 }
354 else
355 {
356 /*
357 * Adding a new variable. Resize the array if required
358 * and then insert the new value at the end.
359 */
360 if (pIntEnv->cVars + 2 > pIntEnv->cAllocated)
361 {
362 void *pvNew = RTMemRealloc(pIntEnv->papszEnv, sizeof(char *) * (pIntEnv->cAllocated + RTENV_GROW_SIZE));
363 if (!pvNew)
364 rc = VERR_NO_MEMORY;
365 else
366 {
367 pIntEnv->papszEnv = (char **)pvNew;
368 pIntEnv->cAllocated += RTENV_GROW_SIZE;
369 for (size_t iNewVar = pIntEnv->cVars; iNewVar < pIntEnv->cAllocated; iNewVar++)
370 pIntEnv->papszEnv[iNewVar] = NULL;
371 }
372 }
373 if (RT_SUCCESS(rc))
374 {
375 pIntEnv->papszEnv[iVar] = pszEntry;
376 pIntEnv->papszEnv[iVar + 1] = NULL; /* this isn't really necessary, but doesn't hurt. */
377 pIntEnv->cVars++;
378 Assert(pIntEnv->cVars == iVar + 1);
379 }
380 }
381
382 RTENV_UNLOCK(pIntEnv);
383
384 if (RT_FAILURE(rc))
385 RTMemFree(pszEntry);
386 }
387 else
388 rc = VERR_NO_MEMORY;
389 }
390 return rc;
391}
392
393
394RTDECL(int) RTEnvUnsetEx(RTENV Env, const char *pszVar)
395{
396 AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
397 AssertPtrReturn(*pszVar, VERR_INVALID_PARAMETER);
398
399 int rc;
400 if (Env == RTENV_DEFAULT)
401 {
402 /*
403 * Since RTEnvUnset isn't UTF-8 clean and actually expects the strings
404 * to be in the current code page (codeset), we'll do the necessary
405 * conversions here.
406 */
407 char *pszVarOtherCP;
408 rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
409 if (RT_SUCCESS(rc))
410 {
411 rc = RTEnvUnset(pszVarOtherCP);
412 RTStrFree(pszVarOtherCP);
413 }
414 }
415 else
416 {
417 PRTENVINTERNAL pIntEnv = Env;
418 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
419 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
420
421 RTENV_LOCK(pIntEnv);
422
423 /*
424 * Remove all variable by the given name.
425 */
426 rc = VINF_ENV_VAR_NOT_FOUND;
427 const size_t cchVar = strlen(pszVar);
428 size_t iVar;
429 for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
430 if ( !strncmp(pIntEnv->papszEnv[iVar], pszVar, cchVar)
431 && pIntEnv->papszEnv[iVar][cchVar] == '=')
432 {
433 RTMemFree(pIntEnv->papszEnv[iVar]);
434 pIntEnv->cVars--;
435 if (pIntEnv->cVars > 0)
436 pIntEnv->papszEnv[iVar] = pIntEnv->papszEnv[pIntEnv->cVars];
437 pIntEnv->papszEnv[pIntEnv->cVars] = NULL;
438 rc = VINF_SUCCESS;
439 /* no break, there could be more. */
440 }
441
442 RTENV_UNLOCK(pIntEnv);
443 }
444 return rc;
445
446}
447
448
449RTDECL(int) RTEnvGetEx(RTENV Env, const char *pszVar, char *pszValue, size_t cbValue, size_t *pcchActual)
450{
451 AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
452 AssertPtrNullReturn(pszValue, VERR_INVALID_POINTER);
453 AssertPtrNullReturn(pcchActual, VERR_INVALID_POINTER);
454 AssertReturn(pcchActual || (pszValue && cbValue), VERR_INVALID_PARAMETER);
455
456 if (pcchActual)
457 *pcchActual = 0;
458 int rc;
459 if (Env == RTENV_DEFAULT)
460 {
461 /*
462 * Since RTEnvGet isn't UTF-8 clean and actually expects the strings
463 * to be in the current code page (codeset), we'll do the necessary
464 * conversions here.
465 */
466 char *pszVarOtherCP;
467 rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
468 if (RT_SUCCESS(rc))
469 {
470 const char *pszValueOtherCP = RTEnvGet(pszVarOtherCP);
471 RTStrFree(pszVarOtherCP);
472 if (pszValueOtherCP)
473 {
474 char *pszValueUtf8;
475 rc = RTStrCurrentCPToUtf8(&pszValueUtf8, pszValueOtherCP);
476 if (RT_SUCCESS(rc))
477 {
478 rc = VINF_SUCCESS;
479 size_t cch = strlen(pszValueUtf8);
480 if (pcchActual)
481 *pcchActual = cch;
482 if (pszValue && cbValue)
483 {
484 if (cch < cbValue)
485 memcpy(pszValue, pszValueUtf8, cch + 1);
486 else
487 rc = VERR_BUFFER_OVERFLOW;
488 }
489 }
490 }
491 else
492 rc = VERR_ENV_VAR_NOT_FOUND;
493 }
494 }
495 else
496 {
497 PRTENVINTERNAL pIntEnv = Env;
498 AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
499 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
500
501 RTENV_LOCK(pIntEnv);
502
503 /*
504 * Locate the first variable and return it to the caller.
505 */
506 rc = VERR_ENV_VAR_NOT_FOUND;
507 const size_t cchVar = strlen(pszVar);
508 size_t iVar;
509 for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
510 if ( !strncmp(pIntEnv->papszEnv[iVar], pszVar, cchVar)
511 && pIntEnv->papszEnv[iVar][cchVar] == '=')
512 {
513 rc = VINF_SUCCESS;
514 const char *pszValueOrg = pIntEnv->papszEnv[iVar] + cchVar + 1;
515 size_t cch = strlen(pszValueOrg);
516 if (pcchActual)
517 *pcchActual = cch;
518 if (pszValue && cbValue)
519 {
520 if (cch < cbValue)
521 memcpy(pszValue, pszValueOrg, cch + 1);
522 else
523 rc = VERR_BUFFER_OVERFLOW;
524 }
525 break;
526 }
527
528 RTENV_UNLOCK(pIntEnv);
529 }
530 return rc;
531
532}
533
534
535RTDECL(bool) RTEnvExistEx(RTENV Env, const char *pszVar)
536{
537 AssertPtrReturn(pszVar, false);
538
539 bool fExist = false;
540 if (Env == RTENV_DEFAULT)
541 {
542 /*
543 * Since RTEnvExist isn't UTF-8 clean and actually expects the strings
544 * to be in the current code page (codeset), we'll do the necessary
545 * conversions here.
546 */
547 char *pszVarOtherCP;
548 int rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
549 if (RT_SUCCESS(rc))
550 {
551 fExist = RTEnvExist(pszVarOtherCP);
552 RTStrFree(pszVarOtherCP);
553 }
554 }
555 else
556 {
557 PRTENVINTERNAL pIntEnv = Env;
558 AssertPtrReturn(pIntEnv, false);
559 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, false);
560
561 RTENV_LOCK(pIntEnv);
562
563 /*
564 * Simple search.
565 */
566 const size_t cchVar = strlen(pszVar);
567 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
568 if ( !strncmp(pIntEnv->papszEnv[iVar], pszVar, cchVar)
569 && pIntEnv->papszEnv[iVar][cchVar] == '=')
570 {
571 fExist = true;
572 break;
573 }
574
575 RTENV_UNLOCK(pIntEnv);
576 }
577 return fExist;
578}
579
580
581RTDECL(char const * const *) RTEnvGetExecEnvP(RTENV Env)
582{
583 const char * const *papszRet;
584 if (Env == RTENV_DEFAULT)
585 {
586 papszRet = rtEnvDefault();
587 if (!papszRet)
588 {
589 static const char * const s_papszDummy[2] = { NULL, NULL };
590 papszRet = &s_papszDummy[0];
591 }
592 }
593 else
594 {
595 PRTENVINTERNAL pIntEnv = Env;
596 AssertPtrReturn(pIntEnv, NULL);
597 AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, NULL);
598
599 RTENV_LOCK(pIntEnv);
600
601 /*
602 * Free any old envp.
603 */
604 if (pIntEnv->papszEnvOtherCP)
605 {
606 for (size_t iVar = 0; pIntEnv->papszEnvOtherCP[iVar]; iVar++)
607 {
608 RTStrFree(pIntEnv->papszEnvOtherCP[iVar]);
609 pIntEnv->papszEnvOtherCP[iVar] = NULL;
610 }
611 RTMemFree(pIntEnv->papszEnvOtherCP);
612 pIntEnv->papszEnvOtherCP = NULL;
613 }
614
615 /*
616 * Construct a new envp with the strings in the process code set.
617 */
618 char **papsz;
619 papszRet = pIntEnv->papszEnvOtherCP = papsz = (char **)RTMemAlloc(sizeof(char *) * (pIntEnv->cVars + 1));
620 if (papsz)
621 {
622 papsz[pIntEnv->cVars] = NULL;
623 for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
624 {
625 int rc = RTStrUtf8ToCurrentCP(&papsz[iVar], pIntEnv->papszEnv[iVar]);
626 if (RT_FAILURE(rc))
627 {
628 /* RTEnvDestroy / we cleans up later. */
629 papsz[iVar] = NULL;
630 AssertRC(rc);
631 papszRet = NULL;
632 break;
633 }
634 }
635 }
636
637 RTENV_UNLOCK(pIntEnv);
638 }
639 return papszRet;
640}
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