[comp.sources.misc] v06i056: setenv.c - a sorted environment package

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (03/08/89)

Posting-number: Volume 6, Issue 56
Submitted-by: Maarten Litmaath <maart@cs.vu.nl>
Archive-name: setenv

setenv() is better than putenv() in 3 ways:

	1) the call is setenv(var, value), which is both easier and more
	   natural than putenv(string)
	   compare
		setenv("HOME", home);
	   with
		strcpy(buf, "HOME=");
		strcat(buf, home);
		putenv(buf);

	2) it isn't an error to invoke setenv() with a local (automatic)
	   variable, because setenv() uses a malloc() + copy scheme (it needn't
	   be blistering fast, you know)

	3) it keeps the environment sorted, both for its own purposes and as a
	   service to the user

Regards & thanks for your attention,
					Maarten Litmaath @ VU Amsterdam:
					maart@cs.vu.nl, mcvax!botter!maart

: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
: --------------------------- cut here --------------------------
PATH=/bin:/usr/bin:/usr/ucb
echo Extracting 'setenv.c'
sed 's/^X//' > 'setenv.c' << '+ END-OF-FILE ''setenv.c'
Xstatic	char	id[] = "@(#)setenv.c 2.1 89/02/22 Maarten Litmaath";
X
X/*
X * setenv.c
X *
X * Sorted environment package.
X *
X * char *setenv(char *var, char *value);
X * Returns a pointer to the new "var=value" string if `value' has been
X * assigned to `var', (char *) NULL if the number of environment variables
X * exceeds MAX_ENV, or `var' is a NULL pointer or a malloc error occurred.
X * If `value' is a NULL pointer, the empty string is assigned to `var'.
X *
X * int unsetenv(char *var);
X * If `var' is the NULL pointer, the complete environment is unset.
X * Returns -1 if the initial number of environment variables exceeds MAX_ENV
X * or a malloc error occurred, else 0.
X *
X * int _envc;
X * The current number of environment variables.
X */
X
X#include	"setenv.h"
X#include	<stdio.h>
X
Xstatic	char	*envbuf[MAX_ENV] = { 0 };
Xstatic	int	initialized = 0, initenv(), envsearch();
Xint	_envc = 0;
Xextern	void	free();
X
X
Xchar	*setenv(var, value)
Xchar	*var, *value;
X{
X	extern	char	*malloc();
X	char	**env, *buf;
X	int	n;
X
X
X	if (!initialized && initenv() == -1)
X		return NULL;
X
X	if (!var)
X		return NULL;
X
X	if (!value)
X		value = "";
X
X	n = strlen(var);
X
X	if (!(buf = malloc(n + strlen(value) + 2)))
X		return NULL;
X
X	(void) sprintf(buf, "%s=%s", var, value);
X
X	if (envsearch(var, n, &env) == 0) {
X		free(*env);			/* unsetenv old value */
X		*env = buf;			/* setenv new value */
X	} else
X		if (_envc == MAX_ENV)
X			return NULL;
X	else
X		if (env == envbuf + _envc++) {
X			*env++ = buf;
X			*env = 0;
X		}
X	else {					/* *env > var */
X		register char	**p, **q;
X
X
X		p = envbuf + _envc;
X		q = p++;
X		while (q > env)
X			*--p = *--q;		/* shift down */
X		*env = buf;			/* insert new var */
X	}
X
X	return buf;
X}
X
X
Xint	unsetenv(var)
Xchar	*var;
X{
X	register char	**p, **q;
X	char	**env;
X
X
X	if (!var)
X		if (!initialized) {
X			environ = envbuf;
X			return 0;
X		} else {
X			for (p = envbuf; *p; )
X				free(*p++);
X			*envbuf = 0;
X			_envc = 0;
X			return 0;
X		}
X
X	if (!initialized && initenv() == -1)
X		return -1;
X
X	if (envsearch(var, strlen(var), &env) == 1)
X		return 0;
X
X	free(*env);			/* unsetenv var */
X
X	p = env++;
X	q = env;
X	while (*p++ = *q++)		/* shift up rest of environment */
X		;
X	--_envc;
X
X	return 0;
X}
X
X
X/*
X * int initenv();
X * Copy environment to private area, sort the copy, set environ to copy,
X * initialize _envc.
X * Return -1 if the environment exceeds MAX_ENV or a malloc error occurred,
X * else 0.
X */
X
Xstatic	int	initenv()
X{
X	register char	**p = environ, **env = envbuf;
X	extern	char	*malloc(), *strcpy();
X	extern	void	qsort();
X	static	int	error = 0;
X	int	istrcmp();
X
X
X	if (error == -1)
X		return -1;
X
X	if (p)
X		while (*p && p < environ + MAX_ENV)
X			if (!(*env = malloc(strlen(*p) + 1)))
X				return error = -1;
X			else
X				(void) strcpy(*env++, *p++);
X
X	if (p >= environ + MAX_ENV)
X		return error = -1;
X
X	*env = 0;
X	_envc = env - envbuf;
X	qsort((char *) envbuf, _envc, sizeof *envbuf, istrcmp);
X	environ = envbuf;
X	initialized = 1;
X	return 0;
X}
X
X
X/*
X * int envsearch(char *var, int n, char ***pos);
X * Binarily search environment for `var', whose length is `n'.
X * If it is present, `*pos' is set to the address of `var' in the environment
X * and 0 is returned, else `*pos' is set to the address of the first variable
X * lexicographically greater than `var', or to the end of the environment,
X * and 1 is returned.
X */
X
Xstatic	int	envsearch(var, n, pos)
Xregister char	*var;
Xregister int	n;
Xchar	***pos;
X{
X	register char	**env, **first = envbuf, **last = envbuf + _envc;
X	register int	m;
X	extern	int	strncmp();
X
X
X	while (first < last) {
X		env = first + ((last - first) >> 1);
X		if ((m = strncmp(*env, var, n)) < 0) {
X			first = env + 1;
X			continue;
X		}
X		if (m > 0) {
X			last = env;
X			continue;
X		}
X		if ((m = (*env)[n] - '=') == 0) {
X			*pos = env;
X			return 0;
X		}
X		if (m < 0) {
X			first = env + 1;
X			continue;
X		}
X		last = env;
X	}
X
X	*pos = last;
X	return 1;
X}
X
X
Xstatic	int	istrcmp(p, q)			/* indirect strcmp */
Xchar	**p, **q;
X{
X	register char	*s1 = *p, *s2 = *q;
X
X	while (*s1 == *s2++)
X		if (!*s1++)
X			return 0;
X	return *s1 - *--s2;
X}
X
X
X#ifdef	STANDALONE
X
Xmain(argc)
Xint	argc;
X{
X	void	set(), unset(), printenv();
X
X
X	printenv();
X
X	if (argc > 1)
X		unset((char *) 0);
X
X	unset("SHELL");
X	unset("PATH");
X	set("SHELL", "/foo/bar/baz");
X	set("FOO", "BAR");
X	unset("FOOO");
X	unset("FO");
X	unset((char *) 0);
X	set("ZORK", (char *) 0);
X	set("TMP", "/tmp");
X}
X
X
Xvoid	set(p, q)
Xchar	*p, *q;
X{
X	void	printenv();
X
X
X	printf("%s -> %s\n\n", p ? p : "(null)", (q = setenv(p, q)) ? q : "?");
X	printenv();
X}
X
X
Xvoid	unset(p)
Xchar	*p;
X{
X	void	printenv();
X
X
X	printf("%s: %d\n\n", p ? p : "(null)", unsetenv(p));
X	printenv();
X}
X
X
Xvoid	printenv()
X{
X	register char	**env;
X
X
X	for (env = environ; *env; ++env)
X		printf("%s\n", *env);
X	printf("\n_envc=%d\n\n", _envc);
X}
X
X#endif	STANDALONE
+ END-OF-FILE setenv.c
chmod 'u=rw,g=r,o=r' 'setenv.c'
set `wc -c 'setenv.c'`
count=$1
case $count in
4740)	:;;
*)	echo 'Bad character count in ''setenv.c' >&2
		echo 'Count should be 4740' >&2
esac
echo Extracting 'setenv.h'
sed 's/^X//' > 'setenv.h' << '+ END-OF-FILE ''setenv.h'
X/*
X * setenv.h
X */
X
X#ifndef	SETENV_H
X#define		SETENV_H
X
X#define		MAX_ENV		256
X
Xextern	char	**environ, *setenv();
Xextern	int	unsetenv(), _envc;
X
X#endif	!SETENV_H
+ END-OF-FILE setenv.h
chmod 'u=rw,g=r,o=r' 'setenv.h'
set `wc -c 'setenv.h'`
count=$1
case $count in
161)	:;;
*)	echo 'Bad character count in ''setenv.h' >&2
		echo 'Count should be 161' >&2
esac
exit 0