/* 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; }