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
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);
|
|
}
|