/*
 * fhist - file history and comparison tools
 * Copyright (C) 1998, 2002, 2008, 2010, 2012 Peter Miller
 *
 * 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 <common/ac/stddef.h>
#include <common/ac/stdlib.h>
#include <common/ac/string.h>
#include <libexplain/malloc.h>
#include <libexplain/realloc.h>
#include <libexplain/strdup.h>

#include <common/env.h>


extern  char    **environ;
static  size_t  nenvirons;
static  int     initialized;


/*
 * NAME
 *      env_initialize - start up environment
 *
 * SYNOPSIS
 *      void env_initialize(void);
 *
 * DESCRIPTION
 *      The env_initialize function is used to copy all of the environment
 *      variables into dynamic memory, so that they may be altered by the
 *      setenv and unsetenv commands.
 */

static void
env_initialize(void)
{
    size_t          j;
    char            **old;

    if (initialized)
        return;
    initialized = 1;
    nenvirons = 0;
    for (j = 0; environ[j]; ++j)
        ++nenvirons;
    old = environ;
    environ = explain_malloc_or_die((nenvirons + 1) * sizeof(char *));
    for (j = 0; j < nenvirons; ++j)
    {
        environ[j] = explain_strdup_or_die(old[j]);
    }
    environ[nenvirons] = 0;
    env_set("SHELL", "/bin/sh");
}


/*
 * NAME
 *      setenv - set environment variable
 *
 * SYNOPSIS
 *      void setenv(char *name, char *value);
 *
 * DESCRIPTION
 *      The setenv function is used to set the given environment variable to
 *      the given value.
 *
 * CAVEAT
 *      Assumes that the env_initialize function has already been called.
 */

void
env_set(const char *name, const char *value)
{
    size_t          name_len;
    size_t          j;
    char            *cp;

    if (!initialized)
        env_initialize();
    cp = 0;
    name_len = strlen(name);
    for (j = 0; j < nenvirons; ++j)
    {
        cp = environ[j];
        /* assert(cp); */
        if
        (
            name_len <= strlen(cp)
        &&
            (cp[name_len] == '=' || !cp[name_len])
        &&
            !memcmp(cp, name, name_len)
        )
            break;
    }
    if (environ[j])
    {
        environ[j] = 0;
        if (cp)
            free(cp);
    }
    else
    {
        size_t          nbytes;

        nbytes = (nenvirons + 2) * sizeof(char *);
        environ = explain_realloc_or_die(environ, nbytes);
        environ[++nenvirons] = 0;
    }
    cp = explain_malloc_or_die(name_len + strlen(value) + 2);
    memcpy(cp, name, name_len);
    cp[name_len] = '=';
    strcpy(cp + name_len + 1, value);
    environ[j] = cp;
}


/*
 * NAME
 *      unsetenv - remove environment variable
 *
 * SYNOPSIS
 *      void unsetenv(char *name);
 *
 * DESCRIPTION
 *      The unsetenv function is used to remove the named variable from the
 *      environment.
 *
 * RETURNS
 *      void
 *
 * CAVEAT
 *      Assumes that the env_initialize function has been called already.
 */

void
env_unset(const char *name)
{
    size_t          name_len;
    size_t          j;
    char            *cp;

    if (!initialized)
        env_initialize();
    name_len = strlen(name);
    cp = 0;
    for (j = 0; j < nenvirons; ++j)
    {
        cp = environ[j];
        /* assert(cp); */
        if
        (
            (cp[name_len] == '=' || !cp[name_len])
        &&
            !strncmp(cp, name, name_len)
        )
            break;
    }
    if (!environ[j])
        return;
    environ[j] = 0;
    if (cp)
        free(cp);
    --nenvirons;
    for ( ; j < nenvirons; ++j)
        environ[j] = environ[j + 1];
}


/* vim: set ts=8 sw=4 et : */
