/* Copyright (C) 2008 Emmanuel Varoquaux
This file is part of XOS.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#include "environ.h"
#include
#include
#define GROW 8
static unsigned int capacity;
/* Dit si un nom de variable d'environnement est valide. */
static inline int isvalid(register const char *name)
{
return name && *name && !strchr(name, '=');
}
/* Dit si un nom correspond a une variable d'environnement. */
static inline char *match(register const char *name, register char *env)
{
while (*name == *env)
name++, env++;
return !*name && *env == '=' ? env + 1 : NULL;
}
/* Retourne le nombre de variables d'environnement. */
static inline unsigned int count()
{
register char **envp;
if (!environ)
return 0;
envp = environ;
while (*envp)
envp++;
return envp - environ;
}
/* Retourne la valeur d'une variable d'environnement.
* Retourne NULL si la variable n'existe pas. */
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;
}
/* Retourne la variable d'environnement.
* Retourne un pointeur sur le NULL terminal de environ si la variable n'existe
* pas.
* Retourne NULL si environ est nul. */
static inline char **getenvp(const char *name)
{
register char **envp;
if (!environ)
return NULL;
envp = environ;
while (*envp && !match(name, *envp))
envp++;
return envp;
}
/* Alloue et construit une variable d'environnement. */
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;
}
/* Libere une variable d'environnement. */
static void freeenv(char *env)
{
if (capacity) /* free() uniquement si l'environnement a ete alloue avec malloc(). */
free(env);
}
/* Duplique environ dans un tableau, qui doit etre de taille suffisante. */
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;
}
/* Assure que environ et son contenu sont alloues avec malloc().
* Si environ est nul, le remplace par un tableau vide.
* Si environ n'a pas ete alloue avec malloc(), le duplique et le remplace. */
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;
}
/* Insere une variable a la fin du tableau. */
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;
}
/* Compacte le tableau a partir d'un trou. */
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;
}