diff options
Diffstat (limited to 'environ.c')
-rw-r--r-- | environ.c | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/environ.c b/environ.c new file mode 100644 index 0000000..74d672e --- /dev/null +++ b/environ.c @@ -0,0 +1,272 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> + +#include <fnmatch.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "tmux.h" + +/* + * Environment - manipulate a set of environment variables. + */ + +RB_HEAD(environ, environ_entry); +static int environ_cmp(struct environ_entry *, struct environ_entry *); +RB_GENERATE_STATIC(environ, environ_entry, entry, environ_cmp); + +static int +environ_cmp(struct environ_entry *envent1, struct environ_entry *envent2) +{ + return (strcmp(envent1->name, envent2->name)); +} + +/* Initialise the environment. */ +struct environ * +environ_create(void) +{ + struct environ *env; + + env = xcalloc(1, sizeof *env); + RB_INIT(env); + + return (env); +} + +/* Free an environment. */ +void +environ_free(struct environ *env) +{ + struct environ_entry *envent, *envent1; + + RB_FOREACH_SAFE(envent, environ, env, envent1) { + RB_REMOVE(environ, env, envent); + free(envent->name); + free(envent->value); + free(envent); + } + free(env); +} + +struct environ_entry * +environ_first(struct environ *env) +{ + return (RB_MIN(environ, env)); +} + +struct environ_entry * +environ_next(struct environ_entry *envent) +{ + return (RB_NEXT(environ, env, envent)); +} + +/* Copy one environment into another. */ +void +environ_copy(struct environ *srcenv, struct environ *dstenv) +{ + struct environ_entry *envent; + + RB_FOREACH(envent, environ, srcenv) { + if (envent->value == NULL) + environ_clear(dstenv, envent->name); + else { + environ_set(dstenv, envent->name, envent->flags, + "%s", envent->value); + } + } +} + +/* Find an environment variable. */ +struct environ_entry * +environ_find(struct environ *env, const char *name) +{ + struct environ_entry envent; + + envent.name = (char *) name; + return (RB_FIND(environ, env, &envent)); +} + +/* Set an environment variable. */ +void +environ_set(struct environ *env, const char *name, int flags, const char *fmt, + ...) +{ + struct environ_entry *envent; + va_list ap; + + va_start(ap, fmt); + if ((envent = environ_find(env, name)) != NULL) { + envent->flags = flags; + free(envent->value); + xvasprintf(&envent->value, fmt, ap); + } else { + envent = xmalloc(sizeof *envent); + envent->name = xstrdup(name); + envent->flags = flags; + xvasprintf(&envent->value, fmt, ap); + RB_INSERT(environ, env, envent); + } + va_end(ap); +} + +/* Clear an environment variable. */ +void +environ_clear(struct environ *env, const char *name) +{ + struct environ_entry *envent; + + if ((envent = environ_find(env, name)) != NULL) { + free(envent->value); + envent->value = NULL; + } else { + envent = xmalloc(sizeof *envent); + envent->name = xstrdup(name); + envent->flags = 0; + envent->value = NULL; + RB_INSERT(environ, env, envent); + } +} + +/* Set an environment variable from a NAME=VALUE string. */ +void +environ_put(struct environ *env, const char *var, int flags) +{ + char *name, *value; + + value = strchr(var, '='); + if (value == NULL) + return; + value++; + + name = xstrdup(var); + name[strcspn(name, "=")] = '\0'; + + environ_set(env, name, flags, "%s", value); + free(name); +} + +/* Unset an environment variable. */ +void +environ_unset(struct environ *env, const char *name) +{ + struct environ_entry *envent; + + if ((envent = environ_find(env, name)) == NULL) + return; + RB_REMOVE(environ, env, envent); + free(envent->name); + free(envent->value); + free(envent); +} + +/* Copy variables from a destination into a source environment. */ +void +environ_update(struct options *oo, struct environ *src, struct environ *dst) +{ + struct environ_entry *envent; + struct options_entry *o; + struct options_array_item *a; + union options_value *ov; + + o = options_get(oo, "update-environment"); + if (o == NULL) + return; + a = options_array_first(o); + while (a != NULL) { + ov = options_array_item_value(a); + RB_FOREACH(envent, environ, src) { + if (fnmatch(ov->string, envent->name, 0) == 0) + break; + } + if (envent == NULL) + environ_clear(dst, ov->string); + else + environ_set(dst, envent->name, 0, "%s", envent->value); + a = options_array_next(a); + } +} + +/* Push environment into the real environment - use after fork(). */ +void +environ_push(struct environ *env) +{ + struct environ_entry *envent; + + environ = xcalloc(1, sizeof *environ); + RB_FOREACH(envent, environ, env) { + if (envent->value != NULL && + *envent->name != '\0' && + (~envent->flags & ENVIRON_HIDDEN)) + setenv(envent->name, envent->value, 1); + } +} + +/* Log the environment. */ +void +environ_log(struct environ *env, const char *fmt, ...) +{ + struct environ_entry *envent; + va_list ap; + char *prefix; + + va_start(ap, fmt); + vasprintf(&prefix, fmt, ap); + va_end(ap); + + RB_FOREACH(envent, environ, env) { + if (envent->value != NULL && *envent->name != '\0') { + log_debug("%s%s=%s", prefix, envent->name, + envent->value); + } + } + + free(prefix); +} + +/* Create initial environment for new child. */ +struct environ * +environ_for_session(struct session *s, int no_TERM) +{ + struct environ *env; + const char *value; + int idx; + + env = environ_create(); + environ_copy(global_environ, env); + if (s != NULL) + environ_copy(s->environ, env); + + if (!no_TERM) { + value = options_get_string(global_options, "default-terminal"); + environ_set(env, "TERM", 0, "%s", value); + environ_set(env, "TERM_PROGRAM", 0, "%s", "tmux"); + environ_set(env, "TERM_PROGRAM_VERSION", 0, "%s", getversion()); + } + + if (s != NULL) + idx = s->id; + else + idx = -1; + environ_set(env, "TMUX", 0, "%s,%ld,%d", socket_path, (long)getpid(), + idx); + + return (env); +} |