VirtualBox

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

Last change on this file since 13040 was 8245, checked in by vboxsync, 17 years ago

rebranding: IPRT files again.

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