/* 
 * procEnviron.c --
 *
 *	Routines to manage a process's environment.  The routines in
 *	this file manage a monitor for the environments.
 *
 * Copyright 1986, 1988 Regents of the University of California
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, provided that the above copyright
 * notice appear in all copies.  The University of California
 * makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without
 * express or implied warranty.
 */

#ifndef lint
static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/proc/procEnviron.c,v 9.2 90/09/12 13:58:02 jhh Exp $ SPRITE (Berkeley)";
#endif /* not lint */

#include <sprite.h>
#include <proc.h>
#include <procInt.h>
#include <sync.h>
#include <sched.h>
#include <sys.h>
#include <stdlib.h>
#include <status.h>
#include <vm.h>
#include <bstring.h>
#include <string.h>
#include <stdio.h>

/*
 * The minimum size of the environment that is allocated.  This size
 * represents the number of environment variables.
 */

#define	MIN_ENVIRON_SIZE	5

/*
 * Structure to describe an internal version of an environment variable.
 */

typedef struct ProcEnvironVar {
    char	*name;
    int		nameLength;
    char	*value;
    int		valueLength;
} ProcEnvironVar;

/*
 * Monitor declarations.
 */

static	Sync_Lock environMonitorLock = 
    Sync_LockInitStatic("Proc:environMonitorLock");
#define	LOCKPTR   &environMonitorLock

static	void	DoCopyEnviron _ARGS_((int srcSize, 
			ProcEnvironVar *srcVarPtr, int destSize, 
			ProcEnvironVar *destVarPtr));
static	void	FreeEnviron _ARGS_((register Proc_EnvironInfo *environPtr,
			int size, Boolean freeEnvironInfo));
static	ProcEnvironVar *FindVar _ARGS_((Proc_EnvironInfo *environPtr,
			char *name));
static 	ProcEnvironVar *InitializeVar _ARGS_((Proc_EnvironInfo 
			*environPtr, char *name, int nameLength));
static	void	DecEnvironRefCount _ARGS_((Proc_EnvironInfo *environPtr));

/*
 * ----------------------------------------------------------------------------
 *
 * Proc_InitMainEnviron --
 *
 *	Allocate an environment for the main process.  This sets things up
 *	so that we don't have to worry about NIL environments.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Environment allocated for main process.
 *
 * ----------------------------------------------------------------------------
 */

ENTRY void
ProcInitMainEnviron(procPtr)
    register	Proc_ControlBlock	*procPtr;	/* Pointer to main's
							 * PCB. */
{
    int		i;

    LOCK_MONITOR;

    procPtr->environPtr =
		(Proc_EnvironInfo *) malloc(sizeof(Proc_EnvironInfo));
    procPtr->environPtr->refCount = 1;
    procPtr->environPtr->size = MIN_ENVIRON_SIZE;
    procPtr->environPtr->varArray = (ProcEnvironVar *) 
		malloc(sizeof(ProcEnvironVar) * MIN_ENVIRON_SIZE);
    for (i = 0; i < MIN_ENVIRON_SIZE; i++) {
	procPtr->environPtr->varArray[i].name = (char *) NIL;
	procPtr->environPtr->varArray[i].value = (char *) NIL;
    }

    UNLOCK_MONITOR;
}


/*
 * ----------------------------------------------------------------------------
 *
 * Proc_SetupEnviron --
 *
 *	Give this process a pointer to the parents environment and increment
 *	the reference count.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	environPtr field of process is set and reference count incremented
 *	in the environment.
 *
 * ----------------------------------------------------------------------------
 */

ENTRY void
ProcSetupEnviron(procPtr)
    register	Proc_ControlBlock	*procPtr;	/* Process to setup
							 * environment for. */
{
    Proc_ControlBlock	*parentProc;

    LOCK_MONITOR;

    parentProc = Proc_GetPCB(procPtr->parentID);
    procPtr->environPtr = parentProc->environPtr;
    procPtr->environPtr->refCount++;

    UNLOCK_MONITOR;
}


/*
 * ----------------------------------------------------------------------------
 *
 * DoCopyEnviron --
 *
 *	Copy the source environment into the destination.  It is assumed that
 *	the destination is at least as big as the source.  Any remaining
 *	variables in the destination environment are set to NIL.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Destination gets copy of source environment with any left over
 *	entries in the destination set to NIL.
 *
 * ----------------------------------------------------------------------------
 */

INTERNAL static void
DoCopyEnviron(srcSize, srcVarPtr, destSize, destVarPtr)
    int					srcSize;	/* # of variables in
							 * source environ. */
    register	ProcEnvironVar	*srcVarPtr;	/* Pointer to source
							 * environ variables.*/
    int					destSize;	/* # of variables in
							 * dest environment. */
    register	ProcEnvironVar	*destVarPtr;	/* Pointer to dest
							 * environ variables. */
{
    int		i;

    for (i = 0; i < srcSize; srcVarPtr++, destVarPtr++, i++) {
	if (srcVarPtr->name != (char *) NIL) {
	    destVarPtr->name = (char *) malloc(srcVarPtr->nameLength + 1);
	    destVarPtr->nameLength = srcVarPtr->nameLength;
	    destVarPtr->value = (char *) malloc(srcVarPtr->valueLength + 1);
	    destVarPtr->valueLength = srcVarPtr->valueLength;
	    bcopy((Address) srcVarPtr->name, (Address) destVarPtr->name,
				srcVarPtr->nameLength + 1);
	    bcopy((Address) srcVarPtr->value, (Address) destVarPtr->value,
				srcVarPtr->valueLength + 1);
	} else {
	    destVarPtr->name = (char *) NIL;
	    destVarPtr->value = (char *) NIL;
	}
    }

    for (; i < destSize; i++, destVarPtr++) {
	destVarPtr->name = (char *) NIL;
	destVarPtr->value = (char *) NIL;
    }
}


/*
 * ----------------------------------------------------------------------------
 *
 * FreeEnviron --
 *
 *	Free up all allocated memory in the environment.  The environment
 *	structure itself is not freed up unless the free flag is set.
 *	The actual number of variables in the environment to free up is given.
 *	This allows the caller to free up an environment that has not 
 *	been totally initialized.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	All memory allocated for the given environment is freed.
 *
 * ----------------------------------------------------------------------------
 */

INTERNAL static void
FreeEnviron(environPtr, size, freeEnvironInfo)
    register	Proc_EnvironInfo	*environPtr;	/* Environment 
							 * to free. */
    int					size;		/* Number of variables
						   	 * in the environ. */
    Boolean				freeEnvironInfo;/* TRUE if should free
							 * environ struct .*/
{
    int					i;
    register	ProcEnvironVar	*varPtr;

    for (i = 0, varPtr = environPtr->varArray; i < size; varPtr++, i++) {
	if (varPtr->name != (char *) NIL) {
	    free((Address) varPtr->name);
	    free((Address) varPtr->value);
	}
    }
    free((Address) environPtr->varArray);

    if (freeEnvironInfo) {
	free((Address) environPtr);
    }
}


/*
 * ----------------------------------------------------------------------------
 *
 * FindVar --
 *
 *	Search the given environment for a variable with the given name.
 *	This assumes that the given name is null terminated.
 *
 * Results:
 *	A pointer to the environment variable if found, NULL otherwise.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

INTERNAL static ProcEnvironVar *
FindVar(environPtr, name)
    Proc_EnvironInfo	*environPtr;	/* Environment to search. */
    char		*name;		/* Name to search for. */
{
    register	ProcEnvironVar	*varPtr;
    int					i;

    for (i = 0, varPtr = environPtr->varArray; 
	 i < environPtr->size; 
	 varPtr++, i++) {
	if (varPtr->name != (char *) NIL && 
	    strcmp(varPtr->name, name) == 0) {
	    return(varPtr);
	}
    }

    return((ProcEnvironVar *) NIL);
}


/*
 * ----------------------------------------------------------------------------
 *
 * InitializeVar --
 *
 *	Put the given name for the environment variable into the given 
 *	environment.  The environment will be expanded if necessary.
 *
 * Results:
 *	A pointer to the initialized environment variable.
 *
 * Side effects:
 *	Environment may be expanded.
 *
 * ----------------------------------------------------------------------------
 */

INTERNAL static ProcEnvironVar *
InitializeVar(environPtr, name, nameLength)
    register Proc_EnvironInfo *environPtr;	/* Environment to put
					       	 * the variable in. */
    char		      *name;	    	/* Name of variable. */
    int			      nameLength;   	/* Length of null terminated 
						 * name not including null 
						 * character. */
{
    register	ProcEnvironVar	*varPtr;
    int					i;
    int					newSize;

    for (i = 0, varPtr = environPtr->varArray; 
	 i < environPtr->size; 
	 varPtr++, i++) {
	if (varPtr->name == (char *) NIL) {
	    break;
	}
    }

    /*
     * If there was no empty space in the environment then
     * make the environment twice as big and set varPtr to point to
     * the first null entry in the environment.
     */

    if (i == environPtr->size) {
	if (environPtr->size == PROC_MAX_ENVIRON_SIZE) {
	    return((ProcEnvironVar *) NIL);
	}
	newSize = environPtr->size * 2;
	if (newSize > PROC_MAX_ENVIRON_SIZE) {
	    newSize = PROC_MAX_ENVIRON_SIZE;
	}
	varPtr = (ProcEnvironVar *) 
			malloc(sizeof(ProcEnvironVar) * newSize);
	DoCopyEnviron(environPtr->size, environPtr->varArray, 
		      newSize, varPtr);
	FreeEnviron(environPtr, environPtr->size, FALSE);
	environPtr->varArray = varPtr;
	varPtr += environPtr->size;
	environPtr->size = newSize;
    }

    /*
     * Store the name in the environment variable.
     */

    varPtr->name = (char *) malloc(nameLength + 1);
    varPtr->nameLength = nameLength;
    bcopy((Address) name, (Address) varPtr->name, nameLength + 1);

    return(varPtr);
}


/*
 * ----------------------------------------------------------------------------
 *
 * DecEnvironRefCount --
 *
 *	Decrement the reference count on the given environment.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Reference count decremented for the environment.
 *
 * ----------------------------------------------------------------------------
 */

static INTERNAL void
DecEnvironRefCount(environPtr)
    register	Proc_EnvironInfo	*environPtr;
{
    environPtr->refCount--;
    if (environPtr->refCount == 0) {
	FreeEnviron(environPtr, environPtr->size, TRUE);
    }
}


/*
 * ----------------------------------------------------------------------------
 *
 * ProcDecEnvironRefCount --
 *
 *	Decrement the reference count on the given environment.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Reference count decremented for the environment.
 *
 * ----------------------------------------------------------------------------
 */

ENTRY void
ProcDecEnvironRefCount(environPtr)
    register	Proc_EnvironInfo	*environPtr;
{
    LOCK_MONITOR;

    DecEnvironRefCount(environPtr);

    UNLOCK_MONITOR;
}


/*
 * ----------------------------------------------------------------------------
 *
 * Proc_SetEnvironStub --
 *
 *	Add the given environment variable to the current process's 
 *	environment.
 *
 * Results:
 *	Return SYS_ARG_NOACCESS if the name or value are inaccessible.
 *
 * Side effects:
 *	The enviroment of the current process is modified.
 *
 * ----------------------------------------------------------------------------
 */

ENTRY ReturnStatus
Proc_SetEnvironStub(environVar)
    Proc_EnvironVar	environVar;	/* Variable to add to environment. */
{
    register	ProcEnvironVar	*varPtr;
    register	Proc_ControlBlock	*procPtr;
    char				*namePtr;
    char				*valuePtr;
    int					nameLength;
    int					valueLength;
    int					nameAccLength;
    int					valueAccLength;
    ReturnStatus			status;

    LOCK_MONITOR;

    procPtr = Proc_GetEffectiveProc();

    /*
     * Make variable name accessible.
     */

    namePtr = environVar.name;
    status = Proc_MakeStringAccessible(PROC_MAX_ENVIRON_NAME_LENGTH,
				       &namePtr, &nameAccLength, &nameLength);
    if (status != SUCCESS) {
	UNLOCK_MONITOR;
	return(status);
    }

    /*
     * Make variable value accessible.
     */

    valuePtr = environVar.value;
    status = Proc_MakeStringAccessible(PROC_MAX_ENVIRON_VALUE_LENGTH,
				       &valuePtr, &valueAccLength, 
				       &valueLength);
    if (status != SUCCESS) {
	Proc_MakeUnaccessible((Address) namePtr, nameAccLength);
	UNLOCK_MONITOR;
	return(status);
    }

    /*
     * See if the variable already exists.  If not then put it in the
     * environment.
     */

    varPtr = FindVar(procPtr->environPtr, namePtr);
    if (varPtr == (ProcEnvironVar *) NIL) {
	varPtr = InitializeVar(procPtr->environPtr, namePtr, nameLength);
	if (varPtr == (ProcEnvironVar *) NIL) {
	    Proc_MakeUnaccessible((Address) namePtr, nameAccLength);
	    Proc_MakeUnaccessible((Address) valuePtr, valueAccLength);
	    UNLOCK_MONITOR;
	    return(PROC_ENVIRON_FULL);
	}
    }
    Proc_MakeUnaccessible((Address) namePtr, nameAccLength);

    /*
     * Put the value of the variable into the environment.
     */

    varPtr->value = (char *) malloc(valueLength + 1);
    varPtr->valueLength = valueLength;
    bcopy((Address) valuePtr, (Address) varPtr->value, valueLength + 1);

    Proc_MakeUnaccessible((Address) valuePtr, valueAccLength);

    UNLOCK_MONITOR;
    return(SUCCESS);
}


/*
 * ----------------------------------------------------------------------------
 *
 * Proc_UnsetEnvironStub --
 *
 *	Remove the given environment variable from the current process's 
 *	environment.
 *
 * Results:
 *	Return PROC_NOT_SET_ENVIRON_VAR if the variable is not set and
 *	SYS_ARG_NOACCESS if the name is not accessible.
 *
 * Side effects:
 *	The enviroment of the current process is modified.
 *
 * ----------------------------------------------------------------------------
 */

ENTRY ReturnStatus
Proc_UnsetEnvironStub(environVar)
    Proc_EnvironVar	environVar;	/* Variable to remove. */
{
    register	ProcEnvironVar	*varPtr;
    register	Proc_ControlBlock	*procPtr;
    char				*namePtr;
    int					nameLength;
    int					nameAccLength;
    ReturnStatus			status;

    LOCK_MONITOR;

    procPtr = Proc_GetEffectiveProc();

    /*
     * Make variable name accessible.
     */

    namePtr = environVar.name;
    status = Proc_MakeStringAccessible(PROC_MAX_ENVIRON_NAME_LENGTH,
				       &namePtr, &nameAccLength, 
				       &nameLength);
    if (status != SUCCESS) {
	UNLOCK_MONITOR;
	return(status);
    }

    /*
     * Find the variable in the environment.
     */

    varPtr = FindVar(procPtr->environPtr, namePtr);
    Proc_MakeUnaccessible((Address) namePtr, nameAccLength);

    if (varPtr == (ProcEnvironVar *) NIL) {
	UNLOCK_MONITOR;
	return(PROC_NOT_SET_ENVIRON_VAR);
    }

    /*
     * Unset the variable by freeing up the space that was allocated
     * for it.
     */

    free((Address) varPtr->name);
    varPtr->name = (char *) NIL;
    free((Address) varPtr->value);
    varPtr->value = (char *) NIL;

    UNLOCK_MONITOR;

    return(SUCCESS);
}


/*
 * ----------------------------------------------------------------------------
 *
 * Proc_GetEnvironVarStub --
 *
 *	Return the value of the given environment variable in the current 
 *	process's environment.
 *
 * Results:
 *	SYS_ARG_NOACCESS if place to store value is a bad address or 
 *	PROC_NOT_SET_ENVIRON_VAR if the environment variable doesn't 
 *	exist.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

ReturnStatus
Proc_GetEnvironVarStub(environVar)
    Proc_EnvironVar	environVar;	/* Variable to retrieve. */
{
    register	ProcEnvironVar	*varPtr;
    register	Proc_ControlBlock	*procPtr;
    char				*namePtr;
    int					nameLength;
    int					nameAccLength;
    ReturnStatus			status;

    LOCK_MONITOR;

    procPtr = Proc_GetEffectiveProc();

    /*
     * Make name accessible.
     */

    namePtr = environVar.name;
    status = Proc_MakeStringAccessible(PROC_MAX_ENVIRON_NAME_LENGTH,
				       &namePtr, &nameAccLength, &nameLength);
    if (status != SUCCESS) {
	UNLOCK_MONITOR;
	return(status);
    }

    /*
     * Find the variable.  If not found then return an error.
     */

    varPtr = FindVar(procPtr->environPtr, namePtr);
    Proc_MakeUnaccessible((Address) namePtr, nameAccLength);

    if (varPtr == (ProcEnvironVar *) NIL) {
	UNLOCK_MONITOR;
	return(PROC_NOT_SET_ENVIRON_VAR);
    }

    /*
     * Copy out the value of the variable.
     */

    if (Proc_ByteCopy(FALSE, varPtr->valueLength + 1, (Address) varPtr->value, 
		    (Address) environVar.value) != SUCCESS) {
	UNLOCK_MONITOR;
	return(SYS_ARG_NOACCESS);
    }

    UNLOCK_MONITOR;
    return(SUCCESS);
}


/*
 * ----------------------------------------------------------------------------
 *
 * Proc_GetEnvironRangeStub --
 *
 *	Return as many environment variables as possible in the given range.  
 *	Variables are numbered from 0.  The actual number of environment 
 *	variables returned is returned in numActualVarsPtr.  The null 
 *	string is returned for any environment variables that are not set.
 *
 * Results:
 *	Error status if some error occcurs.  SUCCESS otherwise.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

ENTRY ReturnStatus
Proc_GetEnvironRangeStub(first, last, envArray, numActualVarsPtr)
    int				first;			/* First var to 
							 * retrieve. */
    int				last;			/* Last var to 
							 * retrieve. */
    register	Proc_EnvironVar	*envArray;		/* Where to 
							 * store vars.*/
    int				*numActualVarsPtr;	/* Number of vars
							 * retrieved. */
{
    Proc_EnvironVar			*saveEnvPtr;
    register	ProcEnvironVar	*varPtr;
    register	Proc_ControlBlock	*procPtr;
    ReturnStatus			status = SUCCESS;
    int					i;
    int					numBytes;
    int					numVars;
    char				nullChar = '\0';

    LOCK_MONITOR;

    if (last < first || first < 0) {
	UNLOCK_MONITOR;
	return(SYS_INVALID_ARG);
    }

    procPtr = Proc_GetEffectiveProc();

    if (first >= procPtr->environPtr->size) {
	numVars = 0;
	status = Vm_CopyOut(sizeof(numVars), 
			(Address) &numVars, (Address) numActualVarsPtr);
	if (status != SUCCESS) {
	    status = SYS_ARG_NOACCESS;
	}
	UNLOCK_MONITOR;
	return(status);
    }
    if (last >= procPtr->environPtr->size) {
	last = procPtr->environPtr->size - 1;
    }

    /*
     * Make the environment array accessible.
     */

    i = (last - first + 1) * sizeof(Proc_EnvironVar);
    Vm_MakeAccessible(VM_READONLY_ACCESS, i,
	    (Address) envArray, &numBytes, (Address *) &saveEnvPtr);
    envArray = saveEnvPtr;
    if (numBytes != i) {
	if (envArray != (Proc_EnvironVar *) NIL) {
	    Vm_MakeUnaccessible((Address) envArray, numBytes);
	}
	UNLOCK_MONITOR;
	return(SYS_ARG_NOACCESS);
    }

    /*
     * Copy out the environment variables.
     */
    for (i = first, varPtr = &(procPtr->environPtr->varArray[first]);
	 i <= last;
	 i++, varPtr++, envArray++) {

	if (varPtr->name == (char *) NIL) {
	    if (Vm_CopyOut(1, (Address) &nullChar, 
			   (Address) envArray->name) != SUCCESS) {
		status = SYS_ARG_NOACCESS;
		break;
	    } else {
		continue;
	    }
	}

	if (Vm_CopyOut(varPtr->nameLength + 1, (Address) varPtr->name, 
		(Address) envArray->name) != SUCCESS) {
	    status = SYS_ARG_NOACCESS;
	    break;
	}

	if (Vm_CopyOut(varPtr->valueLength + 1, (Address) varPtr->value, 
		(Address) envArray->value) != SUCCESS) {
	    status = SYS_ARG_NOACCESS;
	    break;
	}
    }

    Vm_MakeUnaccessible((Address) saveEnvPtr, numBytes);

    if (status == SUCCESS) {
	numVars = last - first + 1;
	if (Vm_CopyOut(sizeof(numVars), (Address) &numVars, 
		       (Address) numActualVarsPtr) != SUCCESS) {
	    status = SYS_ARG_NOACCESS;
	}
    }

    UNLOCK_MONITOR;
    return(status);
}


/*
 * ----------------------------------------------------------------------------
 *
 * Proc_CopyEnvironStub --
 *
 *	Give the current process its own copy of the environment.
 *
 * Results:
 *	Always returns SUCCESS.
 *
 * Side effects:
 *	New environment allocated.
 *
 * ----------------------------------------------------------------------------
 */

ENTRY ReturnStatus
Proc_CopyEnvironStub()
{
    register	Proc_EnvironInfo	*newEnvironPtr;
    register	Proc_ControlBlock	*procPtr;

    LOCK_MONITOR;

    procPtr = Proc_GetEffectiveProc();

    if (procPtr->environPtr->refCount == 1) {
	UNLOCK_MONITOR;
	return(SUCCESS);
    }

    newEnvironPtr = (Proc_EnvironInfo *) malloc(sizeof(Proc_EnvironInfo));
    newEnvironPtr->refCount = 1;
    newEnvironPtr->size = procPtr->environPtr->size;
    newEnvironPtr->varArray = (ProcEnvironVar *) 
		malloc(sizeof(ProcEnvironVar) * newEnvironPtr->size);

    DoCopyEnviron(newEnvironPtr->size, procPtr->environPtr->varArray, 
		  newEnvironPtr->size, newEnvironPtr->varArray);

    DecEnvironRefCount(procPtr->environPtr);
    procPtr->environPtr = newEnvironPtr;

    UNLOCK_MONITOR;

    return(SUCCESS);
}

/*
 * ----------------------------------------------------------------------------
 *
 * Proc_InstallEnvironStub --
 *
 *	Install the given environment as the environment of the current
 *	process.
 *
 * Results:
 *	Error if args invalid or inaccessible.  SUCCESS otherwise.
 *
 * Side effects:
 *	A new enviroment is allocated.
 *
 * ----------------------------------------------------------------------------
 */

ENTRY ReturnStatus
Proc_InstallEnvironStub(environ, numVars)
    Proc_EnvironVar	environ[];	/* Environment to install. */
    int			numVars;	/* Number of variables in the
					 * environment. */
{
    register	Proc_EnvironVar		*userEnvironPtr;
    register	Proc_EnvironInfo	*newEnvironPtr;
    register	ProcEnvironVar	*varPtr;
    Proc_EnvironVar			*saveEnvironPtr;
    int					environLength;
    int					i;
    int					nameAccLength;
    int					nameLength;
    char				*namePtr;
    int					valueAccLength;
    int					valueLength;
    char				*valuePtr;
    ReturnStatus			status;
    Proc_ControlBlock			*procPtr;	

    LOCK_MONITOR;

    if (numVars > PROC_MAX_ENVIRON_SIZE) {
	UNLOCK_MONITOR;
	return(SYS_INVALID_ARG);
    }

    /*
     * Make the users environment array accessible.
     */
    userEnvironPtr = (Proc_EnvironVar *) NIL;
    if (numVars > 0) {
	Vm_MakeAccessible(VM_READONLY_ACCESS, numVars * sizeof(Proc_EnvironVar),
	    		  (Address) environ, &environLength, 
			  (Address *) &saveEnvironPtr);
	userEnvironPtr = saveEnvironPtr;
	if (environLength != numVars * sizeof(Proc_EnvironVar)) {
	    if (userEnvironPtr != (Proc_EnvironVar *) NIL) {
		Vm_MakeUnaccessible((Address) userEnvironPtr, environLength);
	    }
	    UNLOCK_MONITOR;
	    return(SYS_ARG_NOACCESS);
	}
    }

    /*
     * Allocate a new environment of size at least MIN_ENVIRON_SIZE.
     */

    newEnvironPtr = (Proc_EnvironInfo *) malloc(sizeof(Proc_EnvironInfo));
    newEnvironPtr->refCount = 1;
    if (numVars < MIN_ENVIRON_SIZE) {
	newEnvironPtr->size = MIN_ENVIRON_SIZE;
    } else {
	newEnvironPtr->size = numVars;
    }
    newEnvironPtr->varArray = (ProcEnvironVar *) 
		malloc(sizeof(ProcEnvironVar) * newEnvironPtr->size);

    /*
     * Read in the users environment variables and store them in the
     * new environment.
     */

    for (i = 0, varPtr = newEnvironPtr->varArray; 
	 i < numVars;
	 userEnvironPtr++, varPtr++, i++) {
	if (userEnvironPtr->name != (char *) USER_NIL) {
	    namePtr = userEnvironPtr->name;
	    status = Proc_MakeStringAccessible(PROC_MAX_ENVIRON_NAME_LENGTH,
				       &namePtr, &nameAccLength, &nameLength);
	    if (status != SUCCESS) {
		Vm_MakeUnaccessible((Address) saveEnvironPtr, environLength);
		FreeEnviron(newEnvironPtr, i - 1, TRUE);
		UNLOCK_MONITOR;
		return(SYS_ARG_NOACCESS);
	    }
	    valuePtr = userEnvironPtr->value;
	    status = Proc_MakeStringAccessible(PROC_MAX_ENVIRON_VALUE_LENGTH,
				   &valuePtr, &valueAccLength, &valueLength);
	    if (status != SUCCESS) {
		Vm_MakeUnaccessible((Address) saveEnvironPtr, environLength);
		Proc_MakeUnaccessible((Address) namePtr, nameAccLength);
		FreeEnviron(newEnvironPtr, i - 1, TRUE);
		UNLOCK_MONITOR;
		return(SYS_ARG_NOACCESS);
	    }

	    varPtr->name = (char *) malloc(nameLength + 1);
	    varPtr->nameLength = nameLength;
	    bcopy(namePtr, varPtr->name, nameLength + 1);

	    varPtr->value = (char *) malloc(valueLength + 1);
	    varPtr->valueLength = valueLength;
	    bcopy(valuePtr, varPtr->value, valueLength + 1);

	    Proc_MakeUnaccessible((Address) namePtr, nameAccLength);
	    Proc_MakeUnaccessible((Address) valuePtr, valueAccLength);
	} else {
	    varPtr->name = (char *) NIL;
	    varPtr->value = (char *) NIL;
	}
    }

    if (numVars > 0) {
	Vm_MakeUnaccessible((Address) saveEnvironPtr, environLength);
    }

    /*
     * If we allocated more than the user requested then set the excess
     * variables to NIL.
     */

    for (; i < newEnvironPtr->size; i++, varPtr++) {
	varPtr->name = (char *) NIL;
	varPtr->value = (char *) NIL;
    }

    /*
     * Make this new environment the environment of the current process.
     */

    procPtr = Proc_GetEffectiveProc();
    DecEnvironRefCount(procPtr->environPtr);
    procPtr->environPtr = newEnvironPtr;

    UNLOCK_MONITOR;
    return(SUCCESS);
}
