home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tcl8.0 / generic / tclEnv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  17.3 KB  |  695 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tclEnv.c --
  3.  *
  4.  *    Tcl support for environment variables, including a setenv
  5.  *    procedure.  This file contains the generic portion of the
  6.  *    environment module.  It is primarily responsible for keeping
  7.  *    the "env" arrays in sync with the system environment variables.
  8.  *
  9.  * Copyright (c) 1991-1994 The Regents of the University of California.
  10.  * Copyright (c) 1994-1996 Sun Microsystems, Inc.
  11.  *
  12.  * See the file "license.terms" for information on usage and redistribution
  13.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  14.  *
  15.  * SCCS: @(#) tclEnv.c 1.49 97/08/11 20:22:40
  16.  */
  17.  
  18. #include "tclInt.h"
  19. #include "tclPort.h"
  20.  
  21. /*
  22.  * The structure below is used to keep track of all of the interpereters
  23.  * for which we're managing the "env" array.  It's needed so that they
  24.  * can all be updated whenever an environment variable is changed
  25.  * anywhere.
  26.  */
  27.  
  28. typedef struct EnvInterp {
  29.     Tcl_Interp *interp;        /* Interpreter for which we're managing
  30.                  * the env array. */
  31.     struct EnvInterp *nextPtr;    /* Next in list of all such interpreters,
  32.                  * or zero. */
  33. } EnvInterp;
  34.  
  35. static EnvInterp *firstInterpPtr = NULL;
  36.                 /* First in list of all managed interpreters,
  37.                  * or NULL if none. */
  38.  
  39. static int cacheSize = 0;    /* Number of env strings in environCache. */
  40. static char **environCache = NULL;
  41.                 /* Array containing all of the environment
  42.                  * strings that Tcl has allocated. */
  43.  
  44. #ifndef USE_PUTENV
  45. static int environSize = 0;    /* Non-zero means that the environ array was
  46.                  * malloced and has this many total entries
  47.                  * allocated to it (not all may be in use at
  48.                  * once).  Zero means that the environment
  49.                  * array is in its original static state. */
  50. #endif
  51.  
  52. /*
  53.  * Declarations for local procedures defined in this file:
  54.  */
  55.  
  56. static char *        EnvTraceProc _ANSI_ARGS_((ClientData clientData,
  57.                 Tcl_Interp *interp, char *name1, char *name2,
  58.                 int flags));
  59. static int        FindVariable _ANSI_ARGS_((CONST char *name,
  60.                 int *lengthPtr));
  61. static void        ReplaceString _ANSI_ARGS_((CONST char *oldStr,
  62.                 char *newStr));
  63. void            TclSetEnv _ANSI_ARGS_((CONST char *name,
  64.                 CONST char *value));
  65. void            TclUnsetEnv _ANSI_ARGS_((CONST char *name));
  66.  
  67. /*
  68.  *----------------------------------------------------------------------
  69.  *
  70.  * TclSetupEnv --
  71.  *
  72.  *    This procedure is invoked for an interpreter to make environment
  73.  *    variables accessible from that interpreter via the "env"
  74.  *    associative array.
  75.  *
  76.  * Results:
  77.  *    None.
  78.  *
  79.  * Side effects:
  80.  *    The interpreter is added to a list of interpreters managed
  81.  *    by us, so that its view of envariables can be kept consistent
  82.  *    with the view in other interpreters.  If this is the first
  83.  *    call to Tcl_SetupEnv, then additional initialization happens,
  84.  *    such as copying the environment to dynamically-allocated space
  85.  *    for ease of management.
  86.  *
  87.  *----------------------------------------------------------------------
  88.  */
  89.  
  90. void
  91. TclSetupEnv(interp)
  92.     Tcl_Interp *interp;        /* Interpreter whose "env" array is to be
  93.                  * managed. */
  94. {
  95.     EnvInterp *eiPtr;
  96.     char *p, *p2;
  97.     Tcl_DString ds;
  98.     int i, sz;
  99.  
  100. #ifdef MAC_TCL
  101.     if (environ == NULL) {
  102.     environSize = TclMacCreateEnv();
  103.     }
  104. #endif
  105.  
  106.     /*
  107.      * Next, initialize the DString we are going to use for copying
  108.      * the names of the environment variables.
  109.      */
  110.  
  111.     Tcl_DStringInit(&ds);
  112.     
  113.     /*
  114.      * Next, add the interpreter to the list of those that we manage.
  115.      */
  116.  
  117.     eiPtr = (EnvInterp *) ckalloc(sizeof(EnvInterp));
  118.     eiPtr->interp = interp;
  119.     eiPtr->nextPtr = firstInterpPtr;
  120.     firstInterpPtr = eiPtr;
  121.  
  122.     /*
  123.      * Store the environment variable values into the interpreter's
  124.      * "env" array, and arrange for us to be notified on future
  125.      * writes and unsets to that array.
  126.      */
  127.  
  128.     (void) Tcl_UnsetVar2(interp, "env", (char *) NULL, TCL_GLOBAL_ONLY);
  129.     for (i = 0; ; i++) {
  130.     p = environ[i];
  131.     if (p == NULL) {
  132.         break;
  133.     }
  134.     for (p2 = p; *p2 != '='; p2++) {
  135.         if (*p2 == 0) {
  136.         /*
  137.          * This condition doesn't seem like it should ever happen,
  138.          * but it does seem to happen occasionally under some
  139.          * versions of Solaris; ignore the entry.
  140.          */
  141.  
  142.         goto nextEntry;
  143.         }
  144.     }
  145.         sz = p2 - p;
  146.         Tcl_DStringSetLength(&ds, 0);
  147.         Tcl_DStringAppend(&ds, p, sz);
  148.     (void) Tcl_SetVar2(interp, "env", Tcl_DStringValue(&ds),
  149.                 p2+1, TCL_GLOBAL_ONLY);
  150.     nextEntry:
  151.     continue;
  152.     }
  153.     Tcl_TraceVar2(interp, "env", (char *) NULL,
  154.         TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
  155.         EnvTraceProc, (ClientData) NULL);
  156.  
  157.     /*
  158.      * Finally clean up the DString.
  159.      */
  160.  
  161.     Tcl_DStringFree(&ds);
  162. }
  163.  
  164. /*
  165.  *----------------------------------------------------------------------
  166.  *
  167.  * TclSetEnv --
  168.  *
  169.  *    Set an environment variable, replacing an existing value
  170.  *    or creating a new variable if there doesn't exist a variable
  171.  *    by the given name.  This procedure is intended to be a
  172.  *    stand-in for the  UNIX "setenv" procedure so that applications
  173.  *    using that procedure will interface properly to Tcl.  To make
  174.  *    it a stand-in, the Makefile must define "TclSetEnv" to "setenv".
  175.  *
  176.  * Results:
  177.  *    None.
  178.  *
  179.  * Side effects:
  180.  *    The environ array gets updated, as do all of the interpreters
  181.  *    that we manage.
  182.  *
  183.  *----------------------------------------------------------------------
  184.  */
  185.  
  186. void
  187. TclSetEnv(name, value)
  188.     CONST char *name;        /* Name of variable whose value is to be
  189.                  * set. */
  190.     CONST char *value;        /* New value for variable. */
  191. {
  192.     int index, length, nameLength;
  193.     char *p, *oldValue;
  194.     EnvInterp *eiPtr;
  195.  
  196. #ifdef MAC_TCL
  197.     if (environ == NULL) {
  198.     environSize = TclMacCreateEnv();
  199.     }
  200. #endif
  201.  
  202.     /*
  203.      * Figure out where the entry is going to go.  If the name doesn't
  204.      * already exist, enlarge the array if necessary to make room.  If
  205.      * the name exists, free its old entry.
  206.      */
  207.  
  208.     index = FindVariable(name, &length);
  209.     if (index == -1) {
  210. #ifndef USE_PUTENV
  211.     if ((length+2) > environSize) {
  212.         char **newEnviron;
  213.  
  214.         newEnviron = (char **) ckalloc((unsigned)
  215.             ((length+5) * sizeof(char *)));
  216.         memcpy((VOID *) newEnviron, (VOID *) environ,
  217.             length*sizeof(char *));
  218.         if (environSize != 0) {
  219.         ckfree((char *) environ);
  220.         }
  221.         environ = newEnviron;
  222.         environSize = length+5;
  223.     }
  224.     index = length;
  225.     environ[index+1] = NULL;
  226. #endif
  227.     oldValue = NULL;
  228.     nameLength = strlen(name);
  229.     } else {
  230.     /*
  231.      * Compare the new value to the existing value.  If they're
  232.      * the same then quit immediately (e.g. don't rewrite the
  233.      * value or propagate it to other interpreters).  Otherwise,
  234.      * when there are N interpreters there will be N! propagations
  235.      * of the same value among the interpreters.
  236.      */
  237.  
  238.     if (strcmp(value, environ[index]+length+1) == 0) {
  239.         return;
  240.     }
  241.     oldValue = environ[index];
  242.     nameLength = length;
  243.     }
  244.     
  245.  
  246.     /*
  247.      * Update all of the interpreters.
  248.      */
  249.  
  250.     for (eiPtr= firstInterpPtr; eiPtr != NULL; eiPtr = eiPtr->nextPtr) {
  251.     (void) Tcl_SetVar2(eiPtr->interp, "env", (char *) name,
  252.         (char *) value, TCL_GLOBAL_ONLY);
  253.     }
  254.  
  255.     /*
  256.      * Create a new entry.
  257.      */
  258.  
  259.     p = (char *) ckalloc((unsigned) (nameLength + strlen(value) + 2));
  260.     strcpy(p, name);
  261.     p[nameLength] = '=';
  262.     strcpy(p+nameLength+1, value);
  263.  
  264.     /*
  265.      * Update the system environment.
  266.      */
  267.  
  268. #ifdef USE_PUTENV
  269.     putenv(p);
  270. #else
  271.     environ[index] = p;
  272. #endif
  273.  
  274.     /*
  275.      * Replace the old value with the new value in the cache.
  276.      */
  277.  
  278.     ReplaceString(oldValue, p);
  279. }
  280.  
  281. /*
  282.  *----------------------------------------------------------------------
  283.  *
  284.  * Tcl_PutEnv --
  285.  *
  286.  *    Set an environment variable.  Similar to setenv except that
  287.  *    the information is passed in a single string of the form
  288.  *    NAME=value, rather than as separate name strings.  This procedure
  289.  *    is intended to be a stand-in for the  UNIX "putenv" procedure
  290.  *    so that applications using that procedure will interface
  291.  *    properly to Tcl.  To make it a stand-in, the Makefile will
  292.  *    define "Tcl_PutEnv" to "putenv".
  293.  *
  294.  * Results:
  295.  *    None.
  296.  *
  297.  * Side effects:
  298.  *    The environ array gets updated, as do all of the interpreters
  299.  *    that we manage.
  300.  *
  301.  *----------------------------------------------------------------------
  302.  */
  303.  
  304. int
  305. Tcl_PutEnv(string)
  306.     CONST char *string;        /* Info about environment variable in the
  307.                  * form NAME=value. */
  308. {
  309.     int nameLength;
  310.     char *name, *value;
  311.  
  312.     if (string == NULL) {
  313.     return 0;
  314.     }
  315.  
  316.     /*
  317.      * Separate the string into name and value parts, then call
  318.      * TclSetEnv to do all of the real work.
  319.      */
  320.  
  321.     value = strchr(string, '=');
  322.     if (value == NULL) {
  323.     return 0;
  324.     }
  325.     nameLength = value - string;
  326.     if (nameLength == 0) {
  327.     return 0;
  328.     }
  329.     name = (char *) ckalloc((unsigned) nameLength+1);
  330.     memcpy((VOID *) name, (VOID *) string, (size_t) nameLength);
  331.     name[nameLength] = 0;
  332.     TclSetEnv(name, value+1);
  333.     ckfree(name);
  334.     return 0;
  335. }
  336.  
  337. /*
  338.  *----------------------------------------------------------------------
  339.  *
  340.  * TclUnsetEnv --
  341.  *
  342.  *    Remove an environment variable, updating the "env" arrays
  343.  *    in all interpreters managed by us.  This function is intended
  344.  *    to replace the UNIX "unsetenv" function (but to do this the
  345.  *    Makefile must be modified to redefine "TclUnsetEnv" to
  346.  *    "unsetenv".
  347.  *
  348.  * Results:
  349.  *    None.
  350.  *
  351.  * Side effects:
  352.  *    Interpreters are updated, as is environ.
  353.  *
  354.  *----------------------------------------------------------------------
  355.  */
  356.  
  357. void
  358. TclUnsetEnv(name)
  359.     CONST char *name;            /* Name of variable to remove. */
  360. {
  361.     EnvInterp *eiPtr;
  362.     char *oldValue;
  363.     int length, index;
  364. #ifdef USE_PUTENV
  365.     char *string;
  366. #else
  367.     char **envPtr;
  368. #endif
  369.  
  370. #ifdef MAC_TCL
  371.     if (environ == NULL) {
  372.     environSize = TclMacCreateEnv();
  373.     }
  374. #endif
  375.  
  376.     index = FindVariable(name, &length);
  377.  
  378.     /*
  379.      * First make sure that the environment variable exists to avoid
  380.      * doing needless work and to avoid recursion on the unset.
  381.      */
  382.     
  383.     if (index == -1) {
  384.     return;
  385.     }
  386.     /*
  387.      * Remember the old value so we can free it if Tcl created the string.
  388.      */
  389.  
  390.     oldValue = environ[index];
  391.  
  392.     /*
  393.      * Update the system environment.  This must be done before we 
  394.      * update the interpreters or we will recurse.
  395.      */
  396.  
  397. #ifdef USE_PUTENV
  398.     string = ckalloc(length+2);
  399.     memcpy((VOID *) string, (VOID *) name, (size_t) length);
  400.     string[length] = '=';
  401.     string[length+1] = '\0';
  402.     putenv(string);
  403.     ckfree(string);
  404. #else
  405.     for (envPtr = environ+index+1; ; envPtr++) {
  406.     envPtr[-1] = *envPtr;
  407.     if (*envPtr == NULL) {
  408.         break;
  409.     }
  410.     }
  411. #endif
  412.  
  413.     /*
  414.      * Replace the old value in the cache.
  415.      */
  416.  
  417.     ReplaceString(oldValue, NULL);
  418.  
  419.     /*
  420.      * Update all of the interpreters.
  421.      */
  422.  
  423.     for (eiPtr = firstInterpPtr; eiPtr != NULL; eiPtr = eiPtr->nextPtr) {
  424.     (void) Tcl_UnsetVar2(eiPtr->interp, "env", (char *) name,
  425.         TCL_GLOBAL_ONLY);
  426.     }
  427. }
  428.  
  429. /*
  430.  *----------------------------------------------------------------------
  431.  *
  432.  * TclGetEnv --
  433.  *
  434.  *    Retrieve the value of an environment variable.
  435.  *
  436.  * Results:
  437.  *    Returns a pointer to a static string in the environment,
  438.  *    or NULL if the value was not found.
  439.  *
  440.  * Side effects:
  441.  *    None.
  442.  *
  443.  *----------------------------------------------------------------------
  444.  */
  445.  
  446. char *
  447. TclGetEnv(name)
  448.     CONST char *name;        /* Name of variable to find. */
  449. {
  450.     int length, index;
  451.  
  452. #ifdef MAC_TCL
  453.     if (environ == NULL) {
  454.     environSize = TclMacCreateEnv();
  455.     }
  456. #endif
  457.  
  458.     index = FindVariable(name, &length);
  459.     if ((index != -1) &&  (*(environ[index]+length) == '=')) {
  460.     return environ[index]+length+1;
  461.     } else {
  462.     return NULL;
  463.     }
  464. }
  465.  
  466. /*
  467.  *----------------------------------------------------------------------
  468.  *
  469.  * EnvTraceProc --
  470.  *
  471.  *    This procedure is invoked whenever an environment variable
  472.  *    is modified or deleted.  It propagates the change to the
  473.  *    "environ" array and to any other interpreters for whom
  474.  *    we're managing an "env" array.
  475.  *
  476.  * Results:
  477.  *    Always returns NULL to indicate success.
  478.  *
  479.  * Side effects:
  480.  *    Environment variable changes get propagated.  If the whole
  481.  *    "env" array is deleted, then we stop managing things for
  482.  *    this interpreter (usually this happens because the whole
  483.  *    interpreter is being deleted).
  484.  *
  485.  *----------------------------------------------------------------------
  486.  */
  487.  
  488.     /* ARGSUSED */
  489. static char *
  490. EnvTraceProc(clientData, interp, name1, name2, flags)
  491.     ClientData clientData;    /* Not used. */
  492.     Tcl_Interp *interp;        /* Interpreter whose "env" variable is
  493.                  * being modified. */
  494.     char *name1;        /* Better be "env". */
  495.     char *name2;        /* Name of variable being modified, or
  496.                  * NULL if whole array is being deleted. */
  497.     int flags;            /* Indicates what's happening. */
  498. {
  499.     /*
  500.      * First see if the whole "env" variable is being deleted.  If
  501.      * so, just forget about this interpreter.
  502.      */
  503.  
  504.     if (name2 == NULL) {
  505.     register EnvInterp *eiPtr, *prevPtr;
  506.  
  507.     if ((flags & (TCL_TRACE_UNSETS|TCL_TRACE_DESTROYED))
  508.         != (TCL_TRACE_UNSETS|TCL_TRACE_DESTROYED)) {
  509.         panic("EnvTraceProc called with confusing arguments");
  510.     }
  511.     eiPtr = firstInterpPtr;
  512.     if (eiPtr->interp == interp) {
  513.         firstInterpPtr = eiPtr->nextPtr;
  514.     } else {
  515.         for (prevPtr = eiPtr, eiPtr = eiPtr->nextPtr; ;
  516.             prevPtr = eiPtr, eiPtr = eiPtr->nextPtr) {
  517.         if (eiPtr == NULL) {
  518.             panic("EnvTraceProc couldn't find interpreter");
  519.         }
  520.         if (eiPtr->interp == interp) {
  521.             prevPtr->nextPtr = eiPtr->nextPtr;
  522.             break;
  523.         }
  524.         }
  525.     }
  526.     ckfree((char *) eiPtr);
  527.     return NULL;
  528.     }
  529.  
  530.     /*
  531.      * If a value is being set, call TclSetEnv to do all of the work.
  532.      */
  533.  
  534.     if (flags & TCL_TRACE_WRITES) {
  535.     TclSetEnv(name2, Tcl_GetVar2(interp, "env", name2, TCL_GLOBAL_ONLY));
  536.     }
  537.  
  538.     if (flags & TCL_TRACE_UNSETS) {
  539.     TclUnsetEnv(name2);
  540.     }
  541.     return NULL;
  542. }
  543.  
  544. /*
  545.  *----------------------------------------------------------------------
  546.  *
  547.  * ReplaceString --
  548.  *
  549.  *    Replace one string with another in the environment variable
  550.  *    cache.  The cache keeps track of all of the environment
  551.  *    variables that Tcl has modified so they can be freed later.
  552.  *
  553.  * Results:
  554.  *    None.
  555.  *
  556.  * Side effects:
  557.  *    May free the old string.
  558.  *
  559.  *----------------------------------------------------------------------
  560.  */
  561.  
  562. static void
  563. ReplaceString(oldStr, newStr)
  564.     CONST char *oldStr;        /* Old environment string. */
  565.     char *newStr;        /* New environment string. */
  566. {
  567.     int i;
  568.     char **newCache;
  569.  
  570.     /*
  571.      * Check to see if the old value was allocated by Tcl.  If so,
  572.      * it needs to be deallocated to avoid memory leaks.  Note that this
  573.      * algorithm is O(n), not O(1).  This will result in n-squared behavior
  574.      * if lots of environment changes are being made.
  575.      */
  576.  
  577.     for (i = 0; i < cacheSize; i++) {
  578.     if ((environCache[i] == oldStr) || (environCache[i] == NULL)) {
  579.         break;
  580.     }
  581.     }
  582.     if (i < cacheSize) {
  583.     /*
  584.      * Replace or delete the old value.
  585.      */
  586.  
  587.     if (environCache[i]) {
  588.         ckfree(environCache[i]);
  589.     }
  590.         
  591.     if (newStr) {
  592.         environCache[i] = newStr;
  593.     } else {
  594.         for (; i < cacheSize-1; i++) {
  595.         environCache[i] = environCache[i+1];
  596.         }
  597.         environCache[cacheSize-1] = NULL;
  598.     }
  599.     } else {    
  600.     /*
  601.      * We need to grow the cache in order to hold the new string.
  602.      */
  603.  
  604.     newCache = (char **) ckalloc((cacheSize + 5) * sizeof(char *));
  605.     if (environCache) {
  606.         memcpy((VOID *) newCache, (VOID *) environCache,
  607.             (size_t) (cacheSize * sizeof(char*)));
  608.         ckfree((char *) environCache);
  609.     }
  610.     environCache = newCache;
  611.     environCache[cacheSize] = (char *) newStr;
  612.     environCache[cacheSize+1] = NULL;
  613.     cacheSize += 5;
  614.     }
  615. }
  616.  
  617. /*
  618.  *----------------------------------------------------------------------
  619.  *
  620.  * FindVariable --
  621.  *
  622.  *    Locate the entry in environ for a given name.
  623.  *
  624.  * Results:
  625.  *    The return value is the index in environ of an entry with the
  626.  *    name "name", or -1 if there is no such entry.   The integer at
  627.  *    *lengthPtr is filled in with the length of name (if a matching
  628.  *    entry is found) or the length of the environ array (if no matching
  629.  *    entry is found).
  630.  *
  631.  * Side effects:
  632.  *    None.
  633.  *
  634.  *----------------------------------------------------------------------
  635.  */
  636.  
  637. static int
  638. FindVariable(name, lengthPtr)
  639.     CONST char *name;        /* Name of desired environment variable. */
  640.     int *lengthPtr;        /* Used to return length of name (for
  641.                  * successful searches) or number of non-NULL
  642.                  * entries in environ (for unsuccessful
  643.                  * searches). */
  644. {
  645.     int i;
  646.     register CONST char *p1, *p2;
  647.  
  648.     for (i = 0, p1 = environ[i]; p1 != NULL; i++, p1 = environ[i]) {
  649.     for (p2 = name; *p2 == *p1; p1++, p2++) {
  650.         /* NULL loop body. */
  651.     }
  652.     if ((*p1 == '=') && (*p2 == '\0')) {
  653.         *lengthPtr = p2-name;
  654.         return i;
  655.     }
  656.     }
  657.     *lengthPtr = i;
  658.     return -1;
  659. }
  660.  
  661. /*
  662.  *----------------------------------------------------------------------
  663.  *
  664.  * TclFinalizeEnvironment --
  665.  *
  666.  *    This function releases any storage allocated by this module
  667.  *    that isn't still in use by the global environment.  Any
  668.  *    strings that are still in the environment will be leaked.
  669.  *
  670.  * Results:
  671.  *    None.
  672.  *
  673.  * Side effects:
  674.  *    May deallocate storage.
  675.  *
  676.  *----------------------------------------------------------------------
  677.  */
  678.  
  679. void
  680. TclFinalizeEnvironment()
  681. {
  682.     /*
  683.      * For now we just deallocate the cache array and none of the environment
  684.      * strings.  This may leak more memory that strictly necessary, since some
  685.      * of the strings may no longer be in the environment.  However,
  686.      * determining which ones are ok to delete is n-squared, and is pretty
  687.      * unlikely, so we don't bother.
  688.      */
  689.  
  690.     if (environCache) {
  691.     ckfree((char *) environCache);
  692.     environCache = NULL;
  693.     }
  694. }
  695.