View of xos/usr/lib/libc/environ.c


XOS | Parent Directory | View | Download

/* 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 <http://www.gnu.org/licenses/>. */
 
#include "environ.h"
 
#include <string.h>
#include <errno.h>
 
#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;
}