#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)setenv.c 8.1 (Berkeley) 6/4/93";
#endif
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/lib/libc/stdlib/setenv.c,v 1.14 2007/05/01 16:02:41 ache Exp $");
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <crt_externs.h>
#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
struct owned_ptr;
__private_extern__ char *__findenv(const char *, int *, char **);
__private_extern__ int __setenv(const char *, const char *, int, int, char ***, struct owned_ptr *);
__private_extern__ void __unsetenv(const char *, char **, struct owned_ptr *);
__private_extern__ struct owned_ptr *__env_owned;
__private_extern__ int __init__env_owned(int);
#ifdef BUILDING_VARIANT
# ifdef LEGACY_CRT1_ENVIRON
extern char **_saved_environ;
# endif
#else
# ifdef LEGACY_CRT1_ENVIRON
__private_extern__ char **_saved_environ = NULL;
static int
_legacy_crt1_environ(void)
{
if (_saved_environ) *_NSGetEnviron() = _saved_environ;
return 0;
}
int (*_cthread_init_routine)(void) = _legacy_crt1_environ;
# else
static int _do_nothing(void) { return 0; }
int (*_cthread_init_routine)(void) = _do_nothing;
# endif
__private_extern__ struct owned_ptr *__env_owned = NULL;
struct owned_ptr {
const void **table;
int used;
int size;
};
#define OWNED_PTR_INITIAL_SIZE 8
__private_extern__ int _owned_ptr_search(struct owned_ptr * __restrict, const void * __restrict, int * __restrict);
__private_extern__ void
_owned_ptr_add(struct owned_ptr * __restrict owned, const void * __restrict ptr)
{
int index;
if (_owned_ptr_search(owned, ptr, &index) == 0) return;
if (owned->used >= owned->size) {
int new_size = 2 * owned->size;
const void **new_table = (const void **)realloc(owned->table,
new_size * sizeof(const void *));
if (!new_table) {
return;
}
owned->table = new_table;
owned->size = new_size;
}
memmove(owned->table + index + 2, owned->table + index + 1,
sizeof(void *) * (owned->used - index - 1));
owned->table[index + 1] = ptr;
owned->used++;
}
__private_extern__ struct owned_ptr *
_owned_ptr_alloc(void)
{
struct owned_ptr *owned;
owned = (struct owned_ptr *)malloc(sizeof(struct owned_ptr));
if (!owned) return NULL;
owned->table = (const void **)malloc(OWNED_PTR_INITIAL_SIZE *
sizeof(const void *));
if (!owned->table) {
int save = errno;
free(owned);
errno = save;
return NULL;
}
owned->table[0] = NULL;
owned->used = 1;
owned->size = OWNED_PTR_INITIAL_SIZE;
return owned;
}
__private_extern__ void
_owned_ptr_delete(struct owned_ptr *owned, int index)
{
if (!index || index >= owned->used) return;
memmove(owned->table + index, owned->table + index + 1,
sizeof(void *) * (owned->used - index - 1));
owned->used--;
}
__private_extern__ void
_owned_ptr_free(struct owned_ptr *owned)
{
free(owned->table);
free(owned);
}
__private_extern__ int
_owned_ptr_search(struct owned_ptr * __restrict owned, const void * __restrict ptr, int * __restrict result)
{
int low = 0;
int high = owned->used - 1;
int cur;
if (owned->table[high] < ptr) {
if (result) *result = high;
return -1;
} else if (owned->table[high] == ptr) {
if (result) *result = high;
return 0;
}
while (high - low > 1) {
cur = (low + high) / 2;
if (ptr > owned->table[cur]) {
low = cur;
} else if (ptr < owned->table[cur]) {
high = cur;
} else {
if (result) *result = cur;
return 0;
}
}
if (result) *result = low;
return -1;
}
__private_extern__ int
__init__env_owned(int should_set_errno)
{
int save;
if (__env_owned) return 0;
if (!should_set_errno)
save = errno;
__env_owned = _owned_ptr_alloc();
if (!__env_owned) {
if (!should_set_errno)
errno = save;
return -1;
}
return 0;
}
__private_extern__ int
__setenv(name, value, rewrite, copy, environp, owned)
const char *name;
const char *value;
int rewrite, copy;
char ***environp;
struct owned_ptr *owned;
{
char *c;
int offset;
int oindex;
if ((c = __findenv(name, &offset, *environp))) {
char *e;
if (!rewrite)
return (0);
e = (*environp)[offset];
if (_owned_ptr_search(owned, e, &oindex) == 0) {
if (copy > 0) {
size_t l_value = strlen(value);
if (strlen(c) < l_value) {
char *r;
size_t len = c - e;
if ((r = realloc(e, l_value + len + 1)) == NULL)
return (-1);
if (r != e) {
(*environp)[offset] = r;
c = r + len;
_owned_ptr_delete(owned, oindex);
_owned_ptr_add(owned, r);
}
}
while ( (*c++ = *value++) );
return (0);
}
_owned_ptr_delete(owned, oindex);
free(e);
}
} else {
int cnt;
char **p;
for (p = *environp, cnt = 0; *p; ++p, ++cnt);
if (_owned_ptr_search(owned, *environp, &oindex) == 0) {
p = (char **)realloc((char *)*environp,
(size_t)(sizeof(char *) * (cnt + 2)));
if (!p)
return (-1);
if (*environp != p) {
_owned_ptr_delete(owned, oindex);
_owned_ptr_add(owned, p);
*environp = p;
}
}
else {
p = malloc((size_t)(sizeof(char *) * (cnt + 2)));
if (!p)
return (-1);
_owned_ptr_add(owned, p);
bcopy(*environp, p, cnt * sizeof(char *));
*environp = p;
}
(*environp)[cnt + 1] = NULL;
offset = cnt;
}
if (copy > 0) {
for (c = (char *)name; *c && *c != '='; ++c);
if (!((*environp)[offset] =
malloc((size_t)((int)(c - name) + strlen(value) + 2))))
return (-1);
_owned_ptr_add(owned, (*environp)[offset]);
for (c = (*environp)[offset]; (*c = *name++) && *c != '='; ++c);
for (*c++ = '='; (*c++ = *value++); );
} else {
if (copy < 0) {
size_t len = strlen(name);
if((c = malloc(len + 1)) == NULL)
return (-1);
_owned_ptr_add(owned, c);
memcpy(c, name, len + 1);
name = c;
}
(*environp)[offset] = (char *)name;
}
return (0);
}
__private_extern__ void
__unsetenv(const char *name, char **environ, struct owned_ptr *owned)
{
char **p;
int offset;
int oindex;
while (__findenv(name, &offset, environ)) {
if (_owned_ptr_search(owned, environ[offset], &oindex) == 0) {
_owned_ptr_delete(owned, oindex);
free(environ[offset]);
}
for (p = &environ[offset];; ++p)
if (!(*p = *(p + 1)))
break;
}
}
void *
_allocenvstate(void)
{
return _owned_ptr_alloc();
}
char **
_copyenv(char **env)
{
char **p;
int cnt = 1;
if (env)
for (p = env; *p; ++p, ++cnt);
p = (char **)malloc((size_t)(sizeof(char *) * cnt));
if (!p)
return (NULL);
if (env)
bcopy(env, p, cnt * sizeof(char *));
else
*p = NULL;
return p;
}
int
_deallocenvstate(void *state)
{
struct owned_ptr *owned;
if (!(owned = (struct owned_ptr *)state) || owned == __env_owned) {
errno = EINVAL;
return -1;
}
_owned_ptr_free(owned);
return 0;
}
int
_setenvp(const char *name, const char *value, int rewrite, char ***envp, void *state)
{
if (__init__env_owned(1)) return (-1);
return (__setenv(name, value, rewrite, 1, envp, (state ? (struct owned_ptr *)state : __env_owned)));
}
int
_unsetenvp(const char *name, char ***envp, void *state)
{
if (__init__env_owned(1)) return (-1);
__unsetenv(name, *envp, (state ? (struct owned_ptr *)state : __env_owned));
return 0;
}
#endif
int
setenv(name, value, rewrite)
const char *name;
const char *value;
int rewrite;
{
#ifdef LEGACY_CRT1_ENVIRON
int ret;
#endif
if(name == NULL || *name == 0) {
errno = EINVAL;
return (-1);
}
#if __DARWIN_UNIX03
if (strchr(name, '=')) {
errno = EINVAL;
return (-1);
}
#endif
if (*value == '=')
++value;
if (__init__env_owned(1)) return (-1);
#ifdef LEGACY_CRT1_ENVIRON
ret = __setenv(name, value, rewrite, 1, _NSGetEnviron(), __env_owned);
_saved_environ = *_NSGetEnviron();
return ret;
#else
return (__setenv(name, value, rewrite, 1, _NSGetEnviron(), __env_owned));
#endif
}
#if __DARWIN_UNIX03
int
#else
void
#endif
unsetenv(name)
const char *name;
{
#if __DARWIN_UNIX03
if(name == NULL || *name == 0) {
errno = EINVAL;
return (-1);
}
if (strchr(name, '=')) {
errno = EINVAL;
return (-1);
}
if (__init__env_owned(1)) return (-1);
#else
if(name == NULL || *name == 0)
return;
if (__init__env_owned(0)) return;
#endif
__unsetenv(name, *_NSGetEnviron(), __env_owned);
#if __DARWIN_UNIX03
return 0;
#endif
}