You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

208 lines
5.0 KiB

/* -*- coding: utf-8 -*-
* ----------------------------------------------------------------------
* Copyright © 2013, RedJack, LLC.
* All rights reserved.
*
* Please see the COPYING file in this distribution for license
* details.
* ----------------------------------------------------------------------
*/
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "libcork/core.h"
#include "libcork/ds.h"
#include "libcork/os/subprocess.h"
#include "libcork/helpers/errors.h"
#if defined(__APPLE__)
/* Apple doesn't provide access to the "environ" variable from a shared library.
* There's a workaround function to grab the environ pointer described at [1].
*
* [1] http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man7/environ.7.html
*/
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#else
/* On all other POSIX platforms, we assume that environ is available in shared
* libraries. */
extern char **environ;
#endif
struct cork_env_var {
const char *name;
const char *value;
};
static struct cork_env_var *
cork_env_var_new(const char *name, const char *value)
{
struct cork_env_var *var = cork_new(struct cork_env_var);
var->name = cork_strdup(name);
var->value = cork_strdup(value);
return var;
}
static void
cork_env_var_free(void *vvar)
{
struct cork_env_var *var = vvar;
cork_strfree(var->name);
cork_strfree(var->value);
free(var);
}
struct cork_env {
struct cork_hash_table *variables;
struct cork_buffer buffer;
};
struct cork_env *
cork_env_new(void)
{
struct cork_env *env = cork_new(struct cork_env);
env->variables = cork_string_hash_table_new(0, 0);
cork_hash_table_set_free_value(env->variables, cork_env_var_free);
cork_buffer_init(&env->buffer);
return env;
}
static void
cork_env_add_internal(struct cork_env *env, const char *name, const char *value)
{
if (env == NULL) {
setenv(name, value, true);
} else {
struct cork_env_var *var = cork_env_var_new(name, value);
void *old_var;
cork_hash_table_put
(env->variables, (void *) var->name, var, NULL, NULL, &old_var);
if (old_var != NULL) {
cork_env_var_free(old_var);
}
}
}
struct cork_env *
cork_env_clone_current(void)
{
char **curr;
struct cork_env *env = cork_env_new();
for (curr = environ; *curr != NULL; curr++) {
const char *entry = *curr;
const char *equal;
equal = strchr(entry, '=');
if (CORK_UNLIKELY(equal == NULL)) {
/* This environment entry is malformed; skip it. */
continue;
}
/* Make a copy of the name so that it's NUL-terminated rather than
* equal-terminated. */
cork_buffer_set(&env->buffer, entry, equal - entry);
cork_env_add_internal(env, env->buffer.buf, equal + 1);
}
return env;
}
void
cork_env_free(struct cork_env *env)
{
cork_hash_table_free(env->variables);
cork_buffer_done(&env->buffer);
free(env);
}
const char *
cork_env_get(struct cork_env *env, const char *name)
{
if (env == NULL) {
return getenv(name);
} else {
struct cork_env_var *var =
cork_hash_table_get(env->variables, (void *) name);
return (var == NULL)? NULL: var->value;
}
}
void
cork_env_add(struct cork_env *env, const char *name, const char *value)
{
cork_env_add_internal(env, name, value);
}
void
cork_env_add_vprintf(struct cork_env *env, const char *name,
const char *format, va_list args)
{
cork_buffer_vprintf(&env->buffer, format, args);
cork_env_add_internal(env, name, env->buffer.buf);
}
void
cork_env_add_printf(struct cork_env *env, const char *name,
const char *format, ...)
{
va_list args;
va_start(args, format);
cork_env_add_vprintf(env, name, format, args);
va_end(args);
}
void
cork_env_remove(struct cork_env *env, const char *name)
{
if (env == NULL) {
unsetenv(name);
} else {
void *old_var;
cork_hash_table_delete(env->variables, (void *) name, NULL, &old_var);
if (old_var != NULL) {
cork_env_var_free(old_var);
}
}
}
static enum cork_hash_table_map_result
cork_env_set_vars(void *user_data, struct cork_hash_table_entry *entry)
{
struct cork_env_var *var = entry->value;
setenv(var->name, var->value, false);
return CORK_HASH_TABLE_MAP_CONTINUE;
}
#if defined(__APPLE__) || (defined(BSD) && (BSD >= 199103))
/* A handful of platforms [1] don't provide clearenv(), so we must implement our
* own version that clears the environ array directly.
*
* [1] http://www.gnu.org/software/gnulib/manual/html_node/clearenv.html
*/
static void
clearenv(void)
{
*environ = NULL;
}
#else
/* Otherwise assume that we have clearenv available. */
#endif
void
cork_env_replace_current(struct cork_env *env)
{
clearenv();
cork_hash_table_map(env->variables, NULL, cork_env_set_vars);
}