#include "environ.h"
#include <string.h>
#include <errno.h>
#define GROW 8
static unsigned int capacity;
static inline int isvalid(register const char *name)
{
return name && *name && !strchr(name, '=');
}
static inline char *match(register const char *name, register char *env)
{
while (*name == *env)
name++, env++;
return !*name && *env == '=' ? env + 1 : NULL;
}
static inline unsigned int count()
{
register char **envp;
if (!environ)
return 0;
envp = environ;
while (*envp)
envp++;
return envp - environ;
}
static inline char *getvalue(const char *name)
{
register char **envp, *value;
if (!environ)
return NULL;
envp = environ;
while (*envp) {
if ((value = match(name, *envp)))
return value;
envp++;
}
return NULL;
}
static inline char **getenvp(const char *name)
{
register char **envp;
if (!environ)
return NULL;
envp = environ;
while (*envp && !match(name, *envp))
envp++;
return envp;
}
static char *createnv(const char *name, const char *value)
{
size_t namelen, valuelen;
char *env;
namelen = strlen(name);
valuelen = strlen(value);
if (!(env = malloc(namelen + valuelen + 2)))
return NULL;
memcpy(env, name, namelen);
*(env + namelen) = '=';
memcpy(env + namelen + 1, value, valuelen + 1);
return env;
}
static void freeenv(char *env)
{
if (capacity)
free(env);
}
static inline int copy_to(char **new_environ)
{
register char **nenvp, **envp;
int errnum;
nenvp = new_environ;
envp = environ;
while (*envp) {
if (!(*nenvp++ = strdup(*envp))) {
errnum = errno;
goto error_free;
}
envp++;
}
*nenvp = NULL;
return 0;
error_free:
nenvp--;
while (nenvp > new_environ)
free(*--nenvp);
errno = errnum;
return -1;
}
static int materialize()
{
unsigned int new_capacity;
char **new_environ;
int errnum;
if (!environ) {
if (!(environ = malloc(GROW * sizeof (char *))))
return -1;
capacity = GROW;
*environ = NULL;
}
else if (!capacity) {
new_capacity = count() + GROW;
if (!(new_environ = malloc(new_capacity * sizeof (char *))))
return -1;
if (copy_to(new_environ) == -1) {
errnum = errno;
free(new_environ);
errno = errnum;
return -1;
}
environ = new_environ;
capacity = new_capacity;
}
return 0;
}
static int pushenv(char **last, char *env)
{
if ((unsigned int)(last - environ) == capacity - 1) {
if (!(environ = realloc(environ, (capacity + GROW) * sizeof (char *))))
return -1;
capacity += GROW;
last = environ + (capacity - GROW - 1);
}
*last = env;
*(last + 1) = NULL;
return 0;
}
static void compact(char **from)
{
register char **envp;
if (*(from + 1)) {
envp = from + 1;
while (*(envp + 1))
envp++;
*from = *envp;
*envp = NULL;
}
else
*from = NULL;
}
void __environ_init()
{
capacity = 0;
}
void __environ_fini()
{
if (environ && capacity)
free(environ);
}
char __attribute__ ((weak, visibility ("default"))) *getenv(const char *name)
{
if (!isvalid(name))
return NULL;
return getvalue(name);
}
int __attribute__ ((weak, visibility ("default"))) setenv(const char *name, const char *value, int overwrite)
{
char **envp, *env;
if (!isvalid(name)) {
errno = EINVAL;
return -1;
}
if (overwrite)
if (materialize() == -1)
return -1;
envp = getenvp(name);
if (envp && *envp) {
if (overwrite) {
if (!(env = createnv(name, value)))
return -1;
free(*envp);
*envp = env;
}
return 0;
}
else {
if (materialize() == -1)
return -1;
if (!(env = createnv(name, value)))
return -1;
if (pushenv(envp, env) == -1) {
free(env);
return -1;
}
return 0;
}
}
int __attribute__ ((weak, visibility ("default"))) unsetenv(const char *name)
{
char **envp;
if (!isvalid(name)) {
errno = EINVAL;
return -1;
}
envp = getenvp(name);
if (!envp || !*envp)
return 0;
freeenv(*envp);
compact(envp);
return 0;
}