os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclEnv.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
sl@0
     1
/* 
sl@0
     2
 * tclEnv.c --
sl@0
     3
 *
sl@0
     4
 *	Tcl support for environment variables, including a setenv
sl@0
     5
 *	procedure.  This file contains the generic portion of the
sl@0
     6
 *	environment module.  It is primarily responsible for keeping
sl@0
     7
 *	the "env" arrays in sync with the system environment variables.
sl@0
     8
 *
sl@0
     9
 * Copyright (c) 1991-1994 The Regents of the University of California.
sl@0
    10
 * Copyright (c) 1994-1998 Sun Microsystems, Inc.
sl@0
    11
 * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved.  
sl@0
    12
 *
sl@0
    13
 * See the file "license.terms" for information on usage and redistribution
sl@0
    14
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
sl@0
    15
 *
sl@0
    16
 * RCS: @(#) $Id: tclEnv.c,v 1.20.2.3 2006/10/31 22:25:08 das Exp $
sl@0
    17
 */
sl@0
    18
sl@0
    19
#include "tclInt.h"
sl@0
    20
#include "tclPort.h"
sl@0
    21
sl@0
    22
TCL_DECLARE_MUTEX(envMutex)	/* To serialize access to environ */
sl@0
    23
sl@0
    24
static int cacheSize = 0;	/* Number of env strings in environCache. */
sl@0
    25
static char **environCache = NULL;
sl@0
    26
				/* Array containing all of the environment
sl@0
    27
				 * strings that Tcl has allocated. */
sl@0
    28
sl@0
    29
#ifndef USE_PUTENV
sl@0
    30
static char **ourEnviron = NULL;/* Cache of the array that we allocate.
sl@0
    31
				 * We need to track this in case another
sl@0
    32
				 * subsystem swaps around the environ array
sl@0
    33
				 * like we do.
sl@0
    34
				 */
sl@0
    35
static int environSize = 0;	/* Non-zero means that the environ array was
sl@0
    36
				 * malloced and has this many total entries
sl@0
    37
				 * allocated to it (not all may be in use at
sl@0
    38
				 * once).  Zero means that the environment
sl@0
    39
				 * array is in its original static state. */
sl@0
    40
#endif
sl@0
    41
sl@0
    42
/*
sl@0
    43
 * For MacOS X
sl@0
    44
 */
sl@0
    45
#if defined(__APPLE__) && defined(__DYNAMIC__)
sl@0
    46
#include <crt_externs.h>
sl@0
    47
__private_extern__ char **environ;
sl@0
    48
char **environ = NULL;
sl@0
    49
#endif
sl@0
    50
sl@0
    51
/*
sl@0
    52
 * Declarations for local procedures defined in this file:
sl@0
    53
 */
sl@0
    54
sl@0
    55
static char *		EnvTraceProc _ANSI_ARGS_((ClientData clientData,
sl@0
    56
			    Tcl_Interp *interp, CONST char *name1, 
sl@0
    57
			    CONST char *name2, int flags));
sl@0
    58
static void		ReplaceString _ANSI_ARGS_((CONST char *oldStr,
sl@0
    59
			    char *newStr));
sl@0
    60
void			TclSetEnv _ANSI_ARGS_((CONST char *name,
sl@0
    61
			    CONST char *value));
sl@0
    62
void			TclUnsetEnv _ANSI_ARGS_((CONST char *name));
sl@0
    63
sl@0
    64
#if defined (__CYGWIN__) && defined(__WIN32__)
sl@0
    65
static void		TclCygwinPutenv _ANSI_ARGS_((CONST char *string));
sl@0
    66
#endif
sl@0
    67

sl@0
    68
/*
sl@0
    69
 *----------------------------------------------------------------------
sl@0
    70
 *
sl@0
    71
 * TclSetupEnv --
sl@0
    72
 *
sl@0
    73
 *	This procedure is invoked for an interpreter to make environment
sl@0
    74
 *	variables accessible from that interpreter via the "env"
sl@0
    75
 *	associative array.
sl@0
    76
 *
sl@0
    77
 * Results:
sl@0
    78
 *	None.
sl@0
    79
 *
sl@0
    80
 * Side effects:
sl@0
    81
 *	The interpreter is added to a list of interpreters managed
sl@0
    82
 *	by us, so that its view of envariables can be kept consistent
sl@0
    83
 *	with the view in other interpreters.  If this is the first
sl@0
    84
 *	call to TclSetupEnv, then additional initialization happens,
sl@0
    85
 *	such as copying the environment to dynamically-allocated space
sl@0
    86
 *	for ease of management.
sl@0
    87
 *
sl@0
    88
 *----------------------------------------------------------------------
sl@0
    89
 */
sl@0
    90
sl@0
    91
void
sl@0
    92
TclSetupEnv(interp)
sl@0
    93
    Tcl_Interp *interp;		/* Interpreter whose "env" array is to be
sl@0
    94
				 * managed. */
sl@0
    95
{
sl@0
    96
    Tcl_DString envString;
sl@0
    97
    char *p1, *p2;
sl@0
    98
    int i;
sl@0
    99
sl@0
   100
    /*
sl@0
   101
     * For MacOS X
sl@0
   102
     */
sl@0
   103
#if defined(__APPLE__) && defined(__DYNAMIC__)
sl@0
   104
    environ = *_NSGetEnviron();
sl@0
   105
#endif
sl@0
   106
sl@0
   107
    /*
sl@0
   108
     * Synchronize the values in the environ array with the contents
sl@0
   109
     * of the Tcl "env" variable.  To do this:
sl@0
   110
     *    1) Remove the trace that fires when the "env" var is unset.
sl@0
   111
     *    2) Unset the "env" variable.
sl@0
   112
     *    3) If there are no environ variables, create an empty "env"
sl@0
   113
     *       array.  Otherwise populate the array with current values.
sl@0
   114
     *    4) Add a trace that synchronizes the "env" array.
sl@0
   115
     */
sl@0
   116
    
sl@0
   117
    Tcl_UntraceVar2(interp, "env", (char *) NULL,
sl@0
   118
	    TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
sl@0
   119
	    TCL_TRACE_READS | TCL_TRACE_ARRAY,  EnvTraceProc,
sl@0
   120
	    (ClientData) NULL);
sl@0
   121
    
sl@0
   122
    Tcl_UnsetVar2(interp, "env", (char *) NULL, TCL_GLOBAL_ONLY); 
sl@0
   123
    
sl@0
   124
    if (environ[0] == NULL) {
sl@0
   125
	Tcl_Obj *varNamePtr;
sl@0
   126
	
sl@0
   127
	varNamePtr = Tcl_NewStringObj("env", -1);
sl@0
   128
	Tcl_IncrRefCount(varNamePtr);
sl@0
   129
	TclArraySet(interp, varNamePtr, NULL);	
sl@0
   130
	Tcl_DecrRefCount(varNamePtr);
sl@0
   131
    } else {
sl@0
   132
	Tcl_MutexLock(&envMutex);
sl@0
   133
	for (i = 0; environ[i] != NULL; i++) {
sl@0
   134
	    p1 = Tcl_ExternalToUtfDString(NULL, environ[i], -1, &envString);
sl@0
   135
	    p2 = strchr(p1, '=');
sl@0
   136
	    if (p2 == NULL) {
sl@0
   137
		/*
sl@0
   138
		 * This condition seem to happen occasionally under some
sl@0
   139
		 * versions of Solaris; ignore the entry.
sl@0
   140
		 */
sl@0
   141
		
sl@0
   142
		continue;
sl@0
   143
	    }
sl@0
   144
	    p2++;
sl@0
   145
	    p2[-1] = '\0';
sl@0
   146
	    Tcl_SetVar2(interp, "env", p1, p2, TCL_GLOBAL_ONLY);	
sl@0
   147
	    Tcl_DStringFree(&envString);
sl@0
   148
	}
sl@0
   149
	Tcl_MutexUnlock(&envMutex);
sl@0
   150
    }
sl@0
   151
sl@0
   152
    Tcl_TraceVar2(interp, "env", (char *) NULL,
sl@0
   153
	    TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
sl@0
   154
	    TCL_TRACE_READS | TCL_TRACE_ARRAY,  EnvTraceProc,
sl@0
   155
	    (ClientData) NULL);
sl@0
   156
}
sl@0
   157

sl@0
   158
/*
sl@0
   159
 *----------------------------------------------------------------------
sl@0
   160
 *
sl@0
   161
 * TclSetEnv --
sl@0
   162
 *
sl@0
   163
 *	Set an environment variable, replacing an existing value
sl@0
   164
 *	or creating a new variable if there doesn't exist a variable
sl@0
   165
 *	by the given name.  This procedure is intended to be a
sl@0
   166
 *	stand-in for the  UNIX "setenv" procedure so that applications
sl@0
   167
 *	using that procedure will interface properly to Tcl.  To make
sl@0
   168
 *	it a stand-in, the Makefile must define "TclSetEnv" to "setenv".
sl@0
   169
 *
sl@0
   170
 * Results:
sl@0
   171
 *	None.
sl@0
   172
 *
sl@0
   173
 * Side effects:
sl@0
   174
 *	The environ array gets updated.
sl@0
   175
 *
sl@0
   176
 *----------------------------------------------------------------------
sl@0
   177
 */
sl@0
   178
sl@0
   179
void
sl@0
   180
TclSetEnv(name, value)
sl@0
   181
    CONST char *name;		/* Name of variable whose value is to be
sl@0
   182
				 * set (UTF-8). */
sl@0
   183
    CONST char *value;		/* New value for variable (UTF-8). */
sl@0
   184
{
sl@0
   185
    Tcl_DString envString;
sl@0
   186
    int index, length, nameLength;
sl@0
   187
    char *p, *oldValue;
sl@0
   188
    CONST char *p2;
sl@0
   189
sl@0
   190
    /*
sl@0
   191
     * Figure out where the entry is going to go.  If the name doesn't
sl@0
   192
     * already exist, enlarge the array if necessary to make room.  If the
sl@0
   193
     * name exists, free its old entry.
sl@0
   194
     */
sl@0
   195
sl@0
   196
    Tcl_MutexLock(&envMutex);
sl@0
   197
    index = TclpFindVariable(name, &length);
sl@0
   198
sl@0
   199
    if (index == -1) {
sl@0
   200
#ifndef USE_PUTENV
sl@0
   201
	/*
sl@0
   202
	 * We need to handle the case where the environment may be changed
sl@0
   203
	 * outside our control.  environSize is only valid if the current
sl@0
   204
	 * environment is the one we allocated. [Bug 979640]
sl@0
   205
	 */
sl@0
   206
	if ((ourEnviron != environ) || ((length + 2) > environSize)) {
sl@0
   207
	    char **newEnviron;
sl@0
   208
sl@0
   209
	    newEnviron = (char **) ckalloc((unsigned)
sl@0
   210
		    ((length + 5) * sizeof(char *)));
sl@0
   211
	    memcpy((VOID *) newEnviron, (VOID *) environ,
sl@0
   212
		    length*sizeof(char *));
sl@0
   213
	    if ((environSize != 0) && (ourEnviron != NULL)) {
sl@0
   214
		ckfree((char *) ourEnviron);
sl@0
   215
	    }
sl@0
   216
	    environ = ourEnviron = newEnviron;
sl@0
   217
	    environSize = length + 5;
sl@0
   218
#if defined(__APPLE__) && defined(__DYNAMIC__)
sl@0
   219
	    {
sl@0
   220
	    char ***e = _NSGetEnviron();
sl@0
   221
	    *e = environ;
sl@0
   222
	    }
sl@0
   223
#endif
sl@0
   224
	}
sl@0
   225
	index = length;
sl@0
   226
	environ[index + 1] = NULL;
sl@0
   227
#endif
sl@0
   228
	oldValue = NULL;
sl@0
   229
	nameLength = strlen(name);
sl@0
   230
    } else {
sl@0
   231
	CONST char *env;
sl@0
   232
sl@0
   233
	/*
sl@0
   234
	 * Compare the new value to the existing value.  If they're
sl@0
   235
	 * the same then quit immediately (e.g. don't rewrite the
sl@0
   236
	 * value or propagate it to other interpreters).  Otherwise,
sl@0
   237
	 * when there are N interpreters there will be N! propagations
sl@0
   238
	 * of the same value among the interpreters.
sl@0
   239
	 */
sl@0
   240
sl@0
   241
	env = Tcl_ExternalToUtfDString(NULL, environ[index], -1, &envString);
sl@0
   242
	if (strcmp(value, (env + length + 1)) == 0) {
sl@0
   243
	    Tcl_DStringFree(&envString);
sl@0
   244
	    Tcl_MutexUnlock(&envMutex);
sl@0
   245
	    return;
sl@0
   246
	}
sl@0
   247
	Tcl_DStringFree(&envString);
sl@0
   248
sl@0
   249
	oldValue = environ[index];
sl@0
   250
	nameLength = length;
sl@0
   251
    }
sl@0
   252
sl@0
   253
    /*
sl@0
   254
     * Create a new entry.  Build a complete UTF string that contains
sl@0
   255
     * a "name=value" pattern.  Then convert the string to the native
sl@0
   256
     * encoding, and set the environ array value.
sl@0
   257
     */
sl@0
   258
sl@0
   259
    p = (char *) ckalloc((unsigned) (nameLength + strlen(value) + 2));
sl@0
   260
    strcpy(p, name);
sl@0
   261
    p[nameLength] = '=';
sl@0
   262
    strcpy(p+nameLength+1, value);
sl@0
   263
    p2 = Tcl_UtfToExternalDString(NULL, p, -1, &envString);
sl@0
   264
sl@0
   265
    /*
sl@0
   266
     * Copy the native string to heap memory.
sl@0
   267
     */
sl@0
   268
    
sl@0
   269
    p = (char *) ckrealloc(p, (unsigned) (strlen(p2) + 1));
sl@0
   270
    strcpy(p, p2);
sl@0
   271
    Tcl_DStringFree(&envString);
sl@0
   272
sl@0
   273
#ifdef USE_PUTENV
sl@0
   274
    /*
sl@0
   275
     * Update the system environment.
sl@0
   276
     */
sl@0
   277
sl@0
   278
    putenv(p);
sl@0
   279
    index = TclpFindVariable(name, &length);
sl@0
   280
#else
sl@0
   281
    environ[index] = p;
sl@0
   282
#endif
sl@0
   283
sl@0
   284
    /*
sl@0
   285
     * Watch out for versions of putenv that copy the string (e.g. VC++).
sl@0
   286
     * In this case we need to free the string immediately.  Otherwise
sl@0
   287
     * update the string in the cache.
sl@0
   288
     */
sl@0
   289
sl@0
   290
    if ((index != -1) && (environ[index] == p)) {
sl@0
   291
	ReplaceString(oldValue, p);
sl@0
   292
#ifdef HAVE_PUTENV_THAT_COPIES
sl@0
   293
    } else {
sl@0
   294
	/* This putenv() copies instead of taking ownership */
sl@0
   295
	ckfree(p);
sl@0
   296
#endif
sl@0
   297
    }
sl@0
   298
sl@0
   299
    Tcl_MutexUnlock(&envMutex);
sl@0
   300
    
sl@0
   301
    if (!strcmp(name, "HOME")) {
sl@0
   302
	/* 
sl@0
   303
	 * If the user's home directory has changed, we must invalidate
sl@0
   304
	 * the filesystem cache, because '~' expansions will now be
sl@0
   305
	 * incorrect.
sl@0
   306
	 */
sl@0
   307
        Tcl_FSMountsChanged(NULL);
sl@0
   308
    }
sl@0
   309
}
sl@0
   310

sl@0
   311
/*
sl@0
   312
 *----------------------------------------------------------------------
sl@0
   313
 *
sl@0
   314
 * Tcl_PutEnv --
sl@0
   315
 *
sl@0
   316
 *	Set an environment variable.  Similar to setenv except that
sl@0
   317
 *	the information is passed in a single string of the form
sl@0
   318
 *	NAME=value, rather than as separate name strings.  This procedure
sl@0
   319
 *	is intended to be a stand-in for the  UNIX "putenv" procedure
sl@0
   320
 *	so that applications using that procedure will interface
sl@0
   321
 *	properly to Tcl.  To make it a stand-in, the Makefile will
sl@0
   322
 *	define "Tcl_PutEnv" to "putenv".
sl@0
   323
 *
sl@0
   324
 * Results:
sl@0
   325
 *	None.
sl@0
   326
 *
sl@0
   327
 * Side effects:
sl@0
   328
 *	The environ array gets updated, as do all of the interpreters
sl@0
   329
 *	that we manage.
sl@0
   330
 *
sl@0
   331
 *----------------------------------------------------------------------
sl@0
   332
 */
sl@0
   333
sl@0
   334
EXPORT_C int
sl@0
   335
Tcl_PutEnv(string)
sl@0
   336
    CONST char *string;		/* Info about environment variable in the
sl@0
   337
				 * form NAME=value. (native) */
sl@0
   338
{
sl@0
   339
    Tcl_DString nameString;   
sl@0
   340
    CONST char *name;
sl@0
   341
    char *value;
sl@0
   342
sl@0
   343
    if (string == NULL) {
sl@0
   344
	return 0;
sl@0
   345
    }
sl@0
   346
sl@0
   347
    /*
sl@0
   348
     * First convert the native string to UTF.  Then separate the
sl@0
   349
     * string into name and value parts, and call TclSetEnv to do
sl@0
   350
     * all of the real work.
sl@0
   351
     */
sl@0
   352
sl@0
   353
    name = Tcl_ExternalToUtfDString(NULL, string, -1, &nameString);
sl@0
   354
    value = strchr(name, '=');
sl@0
   355
sl@0
   356
    if ((value != NULL) && (value != name)) {
sl@0
   357
	value[0] = '\0';
sl@0
   358
	TclSetEnv(name, value+1);
sl@0
   359
    }
sl@0
   360
sl@0
   361
    Tcl_DStringFree(&nameString);
sl@0
   362
    return 0;
sl@0
   363
}
sl@0
   364

sl@0
   365
/*
sl@0
   366
 *----------------------------------------------------------------------
sl@0
   367
 *
sl@0
   368
 * TclUnsetEnv --
sl@0
   369
 *
sl@0
   370
 *	Remove an environment variable, updating the "env" arrays
sl@0
   371
 *	in all interpreters managed by us.  This function is intended
sl@0
   372
 *	to replace the UNIX "unsetenv" function (but to do this the
sl@0
   373
 *	Makefile must be modified to redefine "TclUnsetEnv" to
sl@0
   374
 *	"unsetenv".
sl@0
   375
 *
sl@0
   376
 * Results:
sl@0
   377
 *	None.
sl@0
   378
 *
sl@0
   379
 * Side effects:
sl@0
   380
 *	Interpreters are updated, as is environ.
sl@0
   381
 *
sl@0
   382
 *----------------------------------------------------------------------
sl@0
   383
 */
sl@0
   384
sl@0
   385
void
sl@0
   386
TclUnsetEnv(name)
sl@0
   387
    CONST char *name;		/* Name of variable to remove (UTF-8). */
sl@0
   388
{
sl@0
   389
    char *oldValue;
sl@0
   390
    int length;
sl@0
   391
    int index;
sl@0
   392
#ifdef USE_PUTENV_FOR_UNSET
sl@0
   393
    Tcl_DString envString;
sl@0
   394
    char *string;
sl@0
   395
#else
sl@0
   396
    char **envPtr;
sl@0
   397
#endif
sl@0
   398
sl@0
   399
    Tcl_MutexLock(&envMutex);
sl@0
   400
    index = TclpFindVariable(name, &length);
sl@0
   401
sl@0
   402
    /*
sl@0
   403
     * First make sure that the environment variable exists to avoid
sl@0
   404
     * doing needless work and to avoid recursion on the unset.
sl@0
   405
     */
sl@0
   406
sl@0
   407
    if (index == -1) {
sl@0
   408
	Tcl_MutexUnlock(&envMutex);
sl@0
   409
	return;
sl@0
   410
    }
sl@0
   411
    /*
sl@0
   412
     * Remember the old value so we can free it if Tcl created the string.
sl@0
   413
     */
sl@0
   414
sl@0
   415
    oldValue = environ[index];
sl@0
   416
sl@0
   417
    /*
sl@0
   418
     * Update the system environment.  This must be done before we 
sl@0
   419
     * update the interpreters or we will recurse.
sl@0
   420
     */
sl@0
   421
sl@0
   422
#ifdef USE_PUTENV_FOR_UNSET
sl@0
   423
    /*
sl@0
   424
     * For those platforms that support putenv to unset, Linux indicates
sl@0
   425
     * that no = should be included, and Windows requires it.
sl@0
   426
     */
sl@0
   427
#ifdef WIN32
sl@0
   428
    string = ckalloc((unsigned int) length+2);
sl@0
   429
    memcpy((VOID *) string, (VOID *) name, (size_t) length);
sl@0
   430
    string[length] = '=';
sl@0
   431
    string[length+1] = '\0';
sl@0
   432
#else
sl@0
   433
    string = ckalloc((unsigned int) length+1);
sl@0
   434
    memcpy((VOID *) string, (VOID *) name, (size_t) length);
sl@0
   435
    string[length] = '\0';
sl@0
   436
#endif
sl@0
   437
sl@0
   438
    Tcl_UtfToExternalDString(NULL, string, -1, &envString);
sl@0
   439
    string = ckrealloc(string, (unsigned) (Tcl_DStringLength(&envString)+1));
sl@0
   440
    strcpy(string, Tcl_DStringValue(&envString));
sl@0
   441
    Tcl_DStringFree(&envString);
sl@0
   442
sl@0
   443
    putenv(string);
sl@0
   444
sl@0
   445
    /*
sl@0
   446
     * Watch out for versions of putenv that copy the string (e.g. VC++).
sl@0
   447
     * In this case we need to free the string immediately.  Otherwise
sl@0
   448
     * update the string in the cache.
sl@0
   449
     */
sl@0
   450
sl@0
   451
    if (environ[index] == string) {
sl@0
   452
	ReplaceString(oldValue, string);
sl@0
   453
#ifdef HAVE_PUTENV_THAT_COPIES
sl@0
   454
    } else {
sl@0
   455
	/* This putenv() copies instead of taking ownership */
sl@0
   456
	ckfree(string);
sl@0
   457
#endif
sl@0
   458
    }
sl@0
   459
#else
sl@0
   460
    for (envPtr = environ+index+1; ; envPtr++) {
sl@0
   461
	envPtr[-1] = *envPtr;
sl@0
   462
	if (*envPtr == NULL) {
sl@0
   463
	    break;
sl@0
   464
	}
sl@0
   465
    }
sl@0
   466
    ReplaceString(oldValue, NULL);
sl@0
   467
#endif
sl@0
   468
sl@0
   469
    Tcl_MutexUnlock(&envMutex);
sl@0
   470
}
sl@0
   471

sl@0
   472
/*
sl@0
   473
 *---------------------------------------------------------------------------
sl@0
   474
 *
sl@0
   475
 * TclGetEnv --
sl@0
   476
 *
sl@0
   477
 *	Retrieve the value of an environment variable.
sl@0
   478
 *
sl@0
   479
 * Results:
sl@0
   480
 *	The result is a pointer to a string specifying the value of the
sl@0
   481
 *	environment variable, or NULL if that environment variable does
sl@0
   482
 *	not exist.  Storage for the result string is allocated in valuePtr;
sl@0
   483
 *	the caller must call Tcl_DStringFree() when the result is no
sl@0
   484
 *	longer needed.
sl@0
   485
 *
sl@0
   486
 * Side effects:
sl@0
   487
 *	None.
sl@0
   488
 *
sl@0
   489
 *----------------------------------------------------------------------
sl@0
   490
 */
sl@0
   491
sl@0
   492
CONST char *
sl@0
   493
TclGetEnv(name, valuePtr)
sl@0
   494
    CONST char *name;		/* Name of environment variable to find
sl@0
   495
				 * (UTF-8). */
sl@0
   496
    Tcl_DString *valuePtr;	/* Uninitialized or free DString in which
sl@0
   497
				 * the value of the environment variable is
sl@0
   498
				 * stored. */
sl@0
   499
{
sl@0
   500
    int length, index;
sl@0
   501
    CONST char *result;
sl@0
   502
sl@0
   503
    Tcl_MutexLock(&envMutex);
sl@0
   504
    index = TclpFindVariable(name, &length);
sl@0
   505
    result = NULL;
sl@0
   506
    if (index != -1) {
sl@0
   507
	Tcl_DString envStr;
sl@0
   508
	
sl@0
   509
	result = Tcl_ExternalToUtfDString(NULL, environ[index], -1, &envStr);
sl@0
   510
	result += length;
sl@0
   511
	if (*result == '=') {
sl@0
   512
	    result++;
sl@0
   513
	    Tcl_DStringInit(valuePtr);
sl@0
   514
	    Tcl_DStringAppend(valuePtr, result, -1);
sl@0
   515
	    result = Tcl_DStringValue(valuePtr);
sl@0
   516
	} else {
sl@0
   517
	    result = NULL;
sl@0
   518
	}
sl@0
   519
	Tcl_DStringFree(&envStr);
sl@0
   520
    }
sl@0
   521
    Tcl_MutexUnlock(&envMutex);
sl@0
   522
    return result;
sl@0
   523
}
sl@0
   524

sl@0
   525
/*
sl@0
   526
 *----------------------------------------------------------------------
sl@0
   527
 *
sl@0
   528
 * EnvTraceProc --
sl@0
   529
 *
sl@0
   530
 *	This procedure is invoked whenever an environment variable
sl@0
   531
 *	is read, modified or deleted.  It propagates the change to the global
sl@0
   532
 *	"environ" array.
sl@0
   533
 *
sl@0
   534
 * Results:
sl@0
   535
 *	Always returns NULL to indicate success.
sl@0
   536
 *
sl@0
   537
 * Side effects:
sl@0
   538
 *	Environment variable changes get propagated.  If the whole
sl@0
   539
 *	"env" array is deleted, then we stop managing things for
sl@0
   540
 *	this interpreter (usually this happens because the whole
sl@0
   541
 *	interpreter is being deleted).
sl@0
   542
 *
sl@0
   543
 *----------------------------------------------------------------------
sl@0
   544
 */
sl@0
   545
sl@0
   546
	/* ARGSUSED */
sl@0
   547
static char *
sl@0
   548
EnvTraceProc(clientData, interp, name1, name2, flags)
sl@0
   549
    ClientData clientData;	/* Not used. */
sl@0
   550
    Tcl_Interp *interp;		/* Interpreter whose "env" variable is
sl@0
   551
				 * being modified. */
sl@0
   552
    CONST char *name1;		/* Better be "env". */
sl@0
   553
    CONST char *name2;		/* Name of variable being modified, or NULL
sl@0
   554
				 * if whole array is being deleted (UTF-8). */
sl@0
   555
    int flags;			/* Indicates what's happening. */
sl@0
   556
{
sl@0
   557
    /*
sl@0
   558
     * For array traces, let TclSetupEnv do all the work.
sl@0
   559
     */
sl@0
   560
sl@0
   561
    if (flags & TCL_TRACE_ARRAY) {
sl@0
   562
	TclSetupEnv(interp);
sl@0
   563
	return NULL;
sl@0
   564
    }
sl@0
   565
sl@0
   566
    /*
sl@0
   567
     * If name2 is NULL, then return and do nothing.
sl@0
   568
     */
sl@0
   569
     
sl@0
   570
    if (name2 == NULL) {
sl@0
   571
	return NULL;
sl@0
   572
    }
sl@0
   573
sl@0
   574
    /*
sl@0
   575
     * If a value is being set, call TclSetEnv to do all of the work.
sl@0
   576
     */
sl@0
   577
sl@0
   578
    if (flags & TCL_TRACE_WRITES) {
sl@0
   579
	CONST char *value;
sl@0
   580
	
sl@0
   581
	value = Tcl_GetVar2(interp, "env", name2, TCL_GLOBAL_ONLY);
sl@0
   582
	TclSetEnv(name2, value);
sl@0
   583
    }
sl@0
   584
sl@0
   585
    /*
sl@0
   586
     * If a value is being read, call TclGetEnv to do all of the work.
sl@0
   587
     */
sl@0
   588
sl@0
   589
    if (flags & TCL_TRACE_READS) {
sl@0
   590
	Tcl_DString valueString;
sl@0
   591
	CONST char *value;
sl@0
   592
sl@0
   593
	value = TclGetEnv(name2, &valueString);
sl@0
   594
	if (value == NULL) {
sl@0
   595
	    return "no such variable";
sl@0
   596
	}
sl@0
   597
	Tcl_SetVar2(interp, name1, name2, value, 0);
sl@0
   598
	Tcl_DStringFree(&valueString);
sl@0
   599
    }
sl@0
   600
sl@0
   601
    /*
sl@0
   602
     * For unset traces, let TclUnsetEnv do all the work.
sl@0
   603
     */
sl@0
   604
sl@0
   605
    if (flags & TCL_TRACE_UNSETS) {
sl@0
   606
	TclUnsetEnv(name2);
sl@0
   607
    }
sl@0
   608
    return NULL;
sl@0
   609
}
sl@0
   610

sl@0
   611
/*
sl@0
   612
 *----------------------------------------------------------------------
sl@0
   613
 *
sl@0
   614
 * ReplaceString --
sl@0
   615
 *
sl@0
   616
 *	Replace one string with another in the environment variable
sl@0
   617
 *	cache.  The cache keeps track of all of the environment
sl@0
   618
 *	variables that Tcl has modified so they can be freed later.
sl@0
   619
 *
sl@0
   620
 * Results:
sl@0
   621
 *	None.
sl@0
   622
 *
sl@0
   623
 * Side effects:
sl@0
   624
 *	May free the old string.
sl@0
   625
 *
sl@0
   626
 *----------------------------------------------------------------------
sl@0
   627
 */
sl@0
   628
sl@0
   629
static void
sl@0
   630
ReplaceString(oldStr, newStr)
sl@0
   631
    CONST char *oldStr;		/* Old environment string. */
sl@0
   632
    char *newStr;		/* New environment string. */
sl@0
   633
{
sl@0
   634
    int i;
sl@0
   635
    char **newCache;
sl@0
   636
sl@0
   637
    /*
sl@0
   638
     * Check to see if the old value was allocated by Tcl.  If so,
sl@0
   639
     * it needs to be deallocated to avoid memory leaks.  Note that this
sl@0
   640
     * algorithm is O(n), not O(1).  This will result in n-squared behavior
sl@0
   641
     * if lots of environment changes are being made.
sl@0
   642
     */
sl@0
   643
sl@0
   644
    for (i = 0; i < cacheSize; i++) {
sl@0
   645
	if ((environCache[i] == oldStr) || (environCache[i] == NULL)) {
sl@0
   646
	    break;
sl@0
   647
	}
sl@0
   648
    }
sl@0
   649
    if (i < cacheSize) {
sl@0
   650
	/*
sl@0
   651
	 * Replace or delete the old value.
sl@0
   652
	 */
sl@0
   653
sl@0
   654
	if (environCache[i]) {
sl@0
   655
	    ckfree(environCache[i]);
sl@0
   656
	}
sl@0
   657
sl@0
   658
	if (newStr) {
sl@0
   659
	    environCache[i] = newStr;
sl@0
   660
	} else {
sl@0
   661
	    for (; i < cacheSize-1; i++) {
sl@0
   662
		environCache[i] = environCache[i+1];
sl@0
   663
	    }
sl@0
   664
	    environCache[cacheSize-1] = NULL;
sl@0
   665
	}
sl@0
   666
    } else {
sl@0
   667
        int allocatedSize = (cacheSize + 5) * sizeof(char *);
sl@0
   668
sl@0
   669
	/*
sl@0
   670
	 * We need to grow the cache in order to hold the new string.
sl@0
   671
	 */
sl@0
   672
sl@0
   673
	newCache = (char **) ckalloc((unsigned) allocatedSize);
sl@0
   674
        (VOID *) memset(newCache, (int) 0, (size_t) allocatedSize);
sl@0
   675
sl@0
   676
	if (environCache) {
sl@0
   677
	    memcpy((VOID *) newCache, (VOID *) environCache,
sl@0
   678
		    (size_t) (cacheSize * sizeof(char*)));
sl@0
   679
	    ckfree((char *) environCache);
sl@0
   680
	}
sl@0
   681
	environCache = newCache;
sl@0
   682
	environCache[cacheSize] = newStr;
sl@0
   683
	environCache[cacheSize+1] = NULL;
sl@0
   684
	cacheSize += 5;
sl@0
   685
    }
sl@0
   686
}
sl@0
   687

sl@0
   688
/*
sl@0
   689
 *----------------------------------------------------------------------
sl@0
   690
 *
sl@0
   691
 * TclFinalizeEnvironment --
sl@0
   692
 *
sl@0
   693
 *	This function releases any storage allocated by this module
sl@0
   694
 *	that isn't still in use by the global environment.  Any
sl@0
   695
 *	strings that are still in the environment will be leaked.
sl@0
   696
 *
sl@0
   697
 * Results:
sl@0
   698
 *	None.
sl@0
   699
 *
sl@0
   700
 * Side effects:
sl@0
   701
 *	May deallocate storage.
sl@0
   702
 *
sl@0
   703
 *----------------------------------------------------------------------
sl@0
   704
 */
sl@0
   705
sl@0
   706
void
sl@0
   707
TclFinalizeEnvironment()
sl@0
   708
{
sl@0
   709
    /*
sl@0
   710
     * For now we just deallocate the cache array and none of the environment
sl@0
   711
     * strings.  This may leak more memory that strictly necessary, since some
sl@0
   712
     * of the strings may no longer be in the environment.  However,
sl@0
   713
     * determining which ones are ok to delete is n-squared, and is pretty
sl@0
   714
     * unlikely, so we don't bother.
sl@0
   715
     */
sl@0
   716
sl@0
   717
    if (environCache) {
sl@0
   718
	ckfree((char *) environCache);
sl@0
   719
	environCache = NULL;
sl@0
   720
	cacheSize    = 0;
sl@0
   721
#ifndef USE_PUTENV
sl@0
   722
	environSize  = 0;
sl@0
   723
#endif
sl@0
   724
    }
sl@0
   725
}
sl@0
   726

sl@0
   727
#if defined(__CYGWIN__) && defined(__WIN32__)
sl@0
   728
sl@0
   729
#include <windows.h>
sl@0
   730
sl@0
   731
/*
sl@0
   732
 * When using cygwin, when an environment variable changes, we need to synch
sl@0
   733
 * with both the cygwin environment (in case the application C code calls
sl@0
   734
 * fork) and the Windows environment (in case the application TCL code calls
sl@0
   735
 * exec, which calls the Windows CreateProcess function).
sl@0
   736
 */
sl@0
   737
sl@0
   738
static void
sl@0
   739
TclCygwinPutenv(str)
sl@0
   740
    const char *str;
sl@0
   741
{
sl@0
   742
    char *name, *value;
sl@0
   743
sl@0
   744
    /* Get the name and value, so that we can change the environment
sl@0
   745
       variable for Windows.  */
sl@0
   746
    name = (char *) alloca (strlen (str) + 1);
sl@0
   747
    strcpy (name, str);
sl@0
   748
    for (value = name; *value != '=' && *value != '\0'; ++value)
sl@0
   749
	;
sl@0
   750
    if (*value == '\0') {
sl@0
   751
	    /* Can't happen.  */
sl@0
   752
	    return;
sl@0
   753
	}
sl@0
   754
    *value = '\0';
sl@0
   755
    ++value;
sl@0
   756
    if (*value == '\0') {
sl@0
   757
	value = NULL;
sl@0
   758
    }
sl@0
   759
sl@0
   760
    /* Set the cygwin environment variable.  */
sl@0
   761
#undef putenv
sl@0
   762
    if (value == NULL) {
sl@0
   763
	unsetenv (name);
sl@0
   764
    } else {
sl@0
   765
	putenv(str);
sl@0
   766
    }
sl@0
   767
sl@0
   768
    /*
sl@0
   769
     * Before changing the environment variable in Windows, if this is PATH,
sl@0
   770
     * we need to convert the value back to a Windows style path.
sl@0
   771
     *
sl@0
   772
     * FIXME: The calling program may know it is running under windows, and
sl@0
   773
     * may have set the path to a Windows path, or, worse, appended or
sl@0
   774
     * prepended a Windows path to PATH.
sl@0
   775
     */
sl@0
   776
    if (strcmp (name, "PATH") != 0) {
sl@0
   777
	/* If this is Path, eliminate any PATH variable, to prevent any
sl@0
   778
	   confusion.  */
sl@0
   779
	if (strcmp (name, "Path") == 0) {
sl@0
   780
	    SetEnvironmentVariable ("PATH", (char *) NULL);
sl@0
   781
	    unsetenv ("PATH");
sl@0
   782
	}
sl@0
   783
sl@0
   784
	SetEnvironmentVariable (name, value);
sl@0
   785
    } else {
sl@0
   786
	char *buf;
sl@0
   787
sl@0
   788
	    /* Eliminate any Path variable, to prevent any confusion.  */
sl@0
   789
	SetEnvironmentVariable ("Path", (char *) NULL);
sl@0
   790
	unsetenv ("Path");
sl@0
   791
sl@0
   792
	if (value == NULL) {
sl@0
   793
	    buf = NULL;
sl@0
   794
	} else {
sl@0
   795
	    int size;
sl@0
   796
sl@0
   797
	    size = cygwin_posix_to_win32_path_list_buf_size (value);
sl@0
   798
	    buf = (char *) alloca (size + 1);
sl@0
   799
	    cygwin_posix_to_win32_path_list (value, buf);
sl@0
   800
	}
sl@0
   801
sl@0
   802
	SetEnvironmentVariable (name, buf);
sl@0
   803
    }
sl@0
   804
}
sl@0
   805
sl@0
   806
#endif /* __CYGWIN__ && __WIN32__ */