| /************************************************************************ |
| * |
| * environ.cpp - definitions of testsuite helpers |
| * |
| * $Id$ |
| * |
| ************************************************************************ |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed |
| * with this work for additional information regarding copyright |
| * ownership. The ASF licenses this file to you under the Apache |
| * License, Version 2.0 (the "License"); you may not use this file |
| * except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| * implied. See the License for the specific language governing |
| * permissions and limitations under the License. |
| * |
| * Copyright 2001-2006 Rogue Wave Software. |
| * |
| **************************************************************************/ |
| |
| // expand _TEST_EXPORT macros |
| #define _RWSTD_TEST_SRC |
| |
| #include <rw_environ.h> |
| |
| #include <assert.h> // for assert |
| #include <stdlib.h> // for getenv, malloc, putenv |
| #include <string.h> // for strchr, strlen, ... |
| |
| |
| extern "C" { |
| |
| // char** environ; |
| |
| #ifndef _RWSTD_NO_PUTENV_CONST_CHAR |
| |
| _RWSTD_DLLIMPORT int putenv (const char*) _LIBC_THROWS (); |
| |
| #else // if defined (_RWSTD_NO_PUTENV_CONST_CHAR) |
| |
| _RWSTD_DLLIMPORT int putenv (char*) _LIBC_THROWS (); |
| |
| #endif // _RWSTD_NO_PUTENV_CONST_CHAR |
| |
| |
| #ifdef _RWSTD_NO_UNSETENV |
| # ifndef _RWSTD_NO_UNSETENV_IN_LIBC |
| |
| _RWSTD_DLLIMPORT int unsetenv (const char*) _LIBC_THROWS (); |
| |
| # undef _RWSTD_NO_UNSETENV |
| |
| # endif // _RWSTD_NO_UNSETENV_IN_LIBC |
| #endif // _RWSTD_NO_UNSETENV |
| |
| } // extern "C" |
| |
| |
| // sets (or unsets) one or more sep-separated environment variables |
| _TEST_EXPORT int |
| rw_putenv (const char* str, int sep /* = -1 */) |
| { |
| int nset = 0; |
| int ret = 0; |
| |
| if (!str) { |
| str = getenv ("RW_PUTENV"); |
| if (str) |
| sep = *str++; |
| else |
| return 0; |
| } |
| |
| // set separator to NUL if it's invalid |
| if (sep < 0 || int (_RWSTD_UCHAR_MAX) < sep) |
| sep = 0; |
| |
| for (const char *pvar = str; pvar && *pvar; ++nset) { |
| |
| // look for separator (or the terminating NUL by default) |
| const char *pend = strchr (pvar, sep); |
| |
| if (0 == pend) |
| pend = pvar + strlen (pvar); |
| |
| const size_t varlen = pend - pvar; |
| |
| // reserve the one more character for Windows mode |
| char* const envvar = (char*)malloc (varlen + 2); |
| if (0 == envvar) |
| return -1; |
| |
| memcpy (envvar, pvar, varlen); |
| envvar [varlen] = '\0'; |
| |
| // look for the first equals sign |
| char* const equals = strchr (envvar, '='); |
| |
| char *var = 0; |
| |
| // putenv mode: 0 - POSIX; 1 - undefined; 2 - Windows |
| static int mode = 1; |
| |
| if (equals) { |
| // add the variable to the environment or modify it if |
| // it's already defined |
| |
| // Note: calling Solaris 7 putenv() during program startup |
| // (i.e., from ctors of namespace-scope objects) prevents |
| // getenv() from finding that variable at program runtime |
| |
| char namebuf [256]; |
| const size_t namelen = equals - envvar; |
| assert (namelen < sizeof (namebuf)); |
| |
| memcpy (namebuf, envvar, namelen); |
| namebuf [namelen] = '\0'; |
| |
| switch (!equals [1] * mode) { |
| case 0: |
| ret = putenv (envvar); |
| break; |
| case 1: |
| ret = putenv (envvar); |
| if (getenv (namebuf)) { |
| mode = 0; |
| break; |
| } |
| mode = 2; |
| // fall through |
| case 2: |
| // on Windows it's impossible to set empty environment variable |
| // append any character after '=' |
| equals [1] = '1'; |
| equals [2] = '\0'; |
| ret = putenv (envvar); |
| equals [1] = '\0'; |
| break; |
| } |
| |
| // determine wheteher putenv() made copy of the variable |
| // or if it simply used the pointer passed to it; if the |
| // former, deallocate the buffer dynamically allocated |
| // above |
| var = getenv (namebuf); |
| |
| // empty the environment variable directly on Windows |
| if (!equals [1] && 2 == mode) |
| *var = '\0'; |
| |
| if (equals + 1 != var) |
| free (envvar); |
| } |
| else if ((var = getenv (envvar))) { |
| // try to remove variable from the environment |
| |
| #ifndef _RWSTD_NO_UNSETENV |
| # if defined (_RWSTD_OS_FREEBSD) || defined (_RWSTD_OS_DARWIN) |
| // FreeBSD (and Darwin) declares void unsetenv(const char*) |
| // http://www.freebsd.org/cgi/man.cgi?query=unsetenv&sektion=3 |
| unsetenv (envvar); |
| ret = 0; |
| # else // !FreeBSD ... |
| ret = unsetenv (envvar); |
| # endif // FreeBSD ... |
| #else // ifdef _RWSTD_NO_UNSETENV |
| switch (mode) { |
| case 0: |
| ret = putenv (envvar); |
| break; |
| case 1: |
| ret = putenv (envvar); |
| if (!getenv (envvar)) { |
| mode = 0; |
| break; |
| } |
| mode = 2; |
| // fall through |
| case 2: |
| // on Windows append '=' character to remove |
| // the environment variable |
| envvar [varlen] = '='; |
| envvar [varlen + 1] = '\0'; |
| ret = putenv (envvar); |
| envvar [varlen] = '\0'; |
| break; |
| } |
| #endif // _RWSTD_NO_UNSETENV |
| |
| if (0 == ret) { |
| // see if the variable has been removed |
| var = getenv (envvar); |
| if (var) { |
| // if not, zero-out the first byte of its name |
| // FIXME: make this more robust, e.g., by calling |
| // unsetenv() when provided or by manipulating |
| // the environment directly |
| *(var - 1 - varlen) = '\0'; |
| |
| #if 0 // disabled |
| |
| char **penv = environ; |
| if (penv) { |
| while (*penv && *penv != (var - 1 - varlen)) |
| ++penv; |
| |
| while (*penv) |
| *penv = penv [1]; |
| } |
| |
| #endif // 0/1 |
| |
| } |
| } |
| |
| free (envvar); |
| } |
| |
| pvar = pend + !!*pend; |
| } |
| |
| if (1 == nset) |
| return ret; |
| |
| return nset; |
| } |