home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / tcl / tcl7.0b1 / tclEnv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-08  |  13.5 KB  |  523 lines

  1. /* 
  2.  * tclEnv.c --
  3.  *
  4.  *    Tcl support for environment variables, including a setenv
  5.  *    procedure.
  6.  *
  7.  * Copyright (c) 1991-1993 The Regents of the University of California.
  8.  * All rights reserved.
  9.  *
  10.  * Permission is hereby granted, without written agreement and without
  11.  * license or royalty fees, to use, copy, modify, and distribute this
  12.  * software and its documentation for any purpose, provided that the
  13.  * above copyright notice and the following two paragraphs appear in
  14.  * all copies of this software.
  15.  * 
  16.  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
  17.  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
  18.  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
  19.  * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  20.  *
  21.  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  22.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  23.  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  24.  * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
  25.  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  26.  */
  27.  
  28. #ifndef lint
  29. static char rcsid[] = "$Header: /user6/ouster/tcl/RCS/tclEnv.c,v 1.14 93/07/08 09:59:27 ouster Exp $ SPRITE (Berkeley)";
  30. #endif /* not lint */
  31.  
  32. /*
  33.  * The putenv definition below causes any system prototype for putenv
  34.  * to be ignored so that there won't be a clash when the version of
  35.  * putenv in this file is compiled.
  36.  */
  37.  
  38. #define putenv ignore_putenv
  39. #include "tclInt.h"
  40. #include "tclUnix.h"
  41. #undef putenv
  42.  
  43. /*
  44.  * The structure below is used to keep track of all of the interpereters
  45.  * for which we're managing the "env" array.  It's needed so that they
  46.  * can all be updated whenever an environment variable is changed
  47.  * anywhere.
  48.  */
  49.  
  50. typedef struct EnvInterp {
  51.     Tcl_Interp *interp;        /* Interpreter for which we're managing
  52.                  * the env array. */
  53.     struct EnvInterp *nextPtr;    /* Next in list of all such interpreters,
  54.                  * or zero. */
  55. } EnvInterp;
  56.  
  57. static EnvInterp *firstInterpPtr;
  58.                 /* First in list of all managed interpreters,
  59.                  * or NULL if none. */
  60.  
  61. static int environSize = 0;    /* Non-zero means that the all of the
  62.                  * environ-related information is malloc-ed
  63.                  * and the environ array itself has this
  64.                  * many total entries allocated to it (not
  65.                  * all may be in use at once).  Zero means
  66.                  * that the environment array is in its
  67.                  * original static state. */
  68.  
  69. /*
  70.  * Declarations for local procedures defined in this file:
  71.  */
  72.  
  73. static void        EnvInit _ANSI_ARGS_((void));
  74. static char *        EnvTraceProc _ANSI_ARGS_((ClientData clientData,
  75.                 Tcl_Interp *interp, char *name1, char *name2,
  76.                 int flags));
  77. static int        FindVariable _ANSI_ARGS_((CONST char *name,
  78.                 int *lengthPtr));
  79. void            setenv _ANSI_ARGS_((CONST char *name,
  80.                 CONST char *value));
  81. void            unsetenv _ANSI_ARGS_((CONST char *name));
  82.  
  83. /*
  84.  *----------------------------------------------------------------------
  85.  *
  86.  * TclSetupEnv --
  87.  *
  88.  *    This procedure is invoked for an interpreter to make environment
  89.  *    variables accessible from that interpreter via the "env"
  90.  *    associative array.
  91.  *
  92.  * Results:
  93.  *    None.
  94.  *
  95.  * Side effects:
  96.  *    The interpreter is added to a list of interpreters managed
  97.  *    by us, so that its view of envariables can be kept consistent
  98.  *    with the view in other interpreters.  If this is the first
  99.  *    call to Tcl_SetupEnv, then additional initialization happens,
  100.  *    such as copying the environment to dynamically-allocated space
  101.  *    for ease of management.
  102.  *
  103.  *----------------------------------------------------------------------
  104.  */
  105.  
  106. void
  107. TclSetupEnv(interp)
  108.     Tcl_Interp *interp;        /* Interpreter whose "env" array is to be
  109.                  * managed. */
  110. {
  111.     EnvInterp *eiPtr;
  112.     int i;
  113.  
  114.     /*
  115.      * First, initialize our environment-related information, if
  116.      * necessary.
  117.      */
  118.  
  119.     if (environSize == 0) {
  120.     EnvInit();
  121.     }
  122.  
  123.     /*
  124.      * Next, add the interpreter to the list of those that we manage.
  125.      */
  126.  
  127.     eiPtr = (EnvInterp *) ckalloc(sizeof(EnvInterp));
  128.     eiPtr->interp = interp;
  129.     eiPtr->nextPtr = firstInterpPtr;
  130.     firstInterpPtr = eiPtr;
  131.  
  132.     /*
  133.      * Store the environment variable values into the interpreter's
  134.      * "env" array, and arrange for us to be notified on future
  135.      * writes and unsets to that array.
  136.      */
  137.  
  138.     (void) Tcl_UnsetVar2(interp, "env", (char *) NULL, TCL_GLOBAL_ONLY);
  139.     for (i = 0; ; i++) {
  140.     char *p, *p2;
  141.  
  142.     p = environ[i];
  143.     if (p == NULL) {
  144.         break;
  145.     }
  146.     for (p2 = p; *p2 != '='; p2++) {
  147.         /* Empty loop body. */
  148.     }
  149.     *p2 = 0;
  150.     (void) Tcl_SetVar2(interp, "env", p, p2+1, TCL_GLOBAL_ONLY);
  151.     *p2 = '=';
  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. /*
  159.  *----------------------------------------------------------------------
  160.  *
  161.  * FindVariable --
  162.  *
  163.  *    Locate the entry in environ for a given name.
  164.  *
  165.  * Results:
  166.  *    The return value is the index in environ of an entry with the
  167.  *    name "name", or -1 if there is no such entry.   The integer at
  168.  *    *lengthPtr is filled in with the length of name (if a matching
  169.  *    entry is found) or the length of the environ array (if no matching
  170.  *    entry is found).
  171.  *
  172.  * Side effects:
  173.  *    None.
  174.  *
  175.  *----------------------------------------------------------------------
  176.  */
  177.  
  178. static int
  179. FindVariable(name, lengthPtr)
  180.     CONST char *name;        /* Name of desired environment variable. */
  181.     int *lengthPtr;        /* Used to return length of name (for
  182.                  * successful searches) or number of non-NULL
  183.                  * entries in environ (for unsuccessful
  184.                  * searches). */
  185. {
  186.     int i;
  187.     CONST register char *p1, *p2;
  188.  
  189.     for (i = 0, p1 = environ[i]; p1 != NULL; i++, p1 = environ[i]) {
  190.     for (p2 = name; *p2 == *p1; p1++, p2++) {
  191.         /* NULL loop body. */
  192.     }
  193.     if ((*p1 == '=') && (*p2 == '\0')) {
  194.         *lengthPtr = p2-name;
  195.         return i;
  196.     }
  197.     }
  198.     *lengthPtr = i;
  199.     return -1;
  200. }
  201.  
  202. /*
  203.  *----------------------------------------------------------------------
  204.  *
  205.  * setenv --
  206.  *
  207.  *    Set an environment variable, replacing an existing value
  208.  *    or creating a new variable if there doesn't exist a variable
  209.  *    by the given name.
  210.  *
  211.  * Results:
  212.  *    None.
  213.  *
  214.  * Side effects:
  215.  *    The environ array gets updated, as do all of the interpreters
  216.  *    that we manage.
  217.  *
  218.  *----------------------------------------------------------------------
  219.  */
  220.  
  221. void
  222. setenv(name, value)
  223.     CONST char *name;        /* Name of variable whose value is to be
  224.                  * set. */
  225.     CONST char *value;        /* New value for variable. */
  226. {
  227.     int index, length, nameLength;
  228.     char *p;
  229.     EnvInterp *eiPtr;
  230.  
  231.     if (environSize == 0) {
  232.     EnvInit();
  233.     }
  234.  
  235.     /*
  236.      * Figure out where the entry is going to go.  If the name doesn't
  237.      * already exist, enlarge the array if necessary to make room.  If
  238.      * the name exists, free its old entry.
  239.      */
  240.  
  241.     index = FindVariable(name, &length);
  242.     if (index == -1) {
  243.     if ((length+2) > environSize) {
  244.         char **newEnviron;
  245.  
  246.         newEnviron = (char **) ckalloc((unsigned)
  247.             ((length+5) * sizeof(char *)));
  248.         memcpy((VOID *) newEnviron, (VOID *) environ,
  249.             length*sizeof(char *));
  250.         ckfree((char *) environ);
  251.         environ = newEnviron;
  252.         environSize = length+5;
  253.     }
  254.     index = length;
  255.     environ[index+1] = NULL;
  256.     nameLength = strlen(name);
  257.     } else {
  258.     /*
  259.      * Compare the new value to the existing value.  If they're
  260.      * the same then quit immediately (e.g. don't rewrite the
  261.      * value or propagate it to other interpeters).  Otherwise,
  262.      * when there are N interpreters there will be N! propagations
  263.      * of the same value among the interpreters.
  264.      */
  265.  
  266.     if (strcmp(value, environ[index]+length+1) == 0) {
  267.         return;
  268.     }
  269.     ckfree(environ[index]);
  270.     nameLength = length;
  271.     }
  272.  
  273.     /*
  274.      * Create a new entry and enter it into the table.
  275.      */
  276.  
  277.     p = (char *) ckalloc((unsigned) (nameLength + strlen(value) + 2));
  278.     environ[index] = p;
  279.     strcpy(p, name);
  280.     p += nameLength;
  281.     *p = '=';
  282.     strcpy(p+1, value);
  283.  
  284.     /*
  285.      * Update all of the interpreters.
  286.      */
  287.  
  288.     for (eiPtr= firstInterpPtr; eiPtr != NULL; eiPtr = eiPtr->nextPtr) {
  289.     (void) Tcl_SetVar2(eiPtr->interp, "env", (char *) name,
  290.         p+1, TCL_GLOBAL_ONLY);
  291.     }
  292. }
  293.  
  294. /*
  295.  *----------------------------------------------------------------------
  296.  *
  297.  * putenv --
  298.  *
  299.  *    Set an environment variable.  Similar to setenv except that
  300.  *    the information is passed in a single string of the form
  301.  *    NAME=value, rather than as separate name strings.  This procedure
  302.  *    is a stand-in for the standard UNIX procedure by the same name,
  303.  *    so that applications using that procedure will interface
  304.  *    properly to Tcl.
  305.  *
  306.  * Results:
  307.  *    None.
  308.  *
  309.  * Side effects:
  310.  *    The environ array gets updated, as do all of the interpreters
  311.  *    that we manage.
  312.  *
  313.  *----------------------------------------------------------------------
  314.  */
  315.  
  316. int
  317. putenv(string)
  318.     CONST char *string;        /* Info about environment variable in the
  319.                  * form NAME=value. */
  320. {
  321.     int nameLength;
  322.     char *name, *value;
  323.  
  324.     if (string == NULL) {
  325.     return 0;
  326.     }
  327.  
  328.     /*
  329.      * Separate the string into name and value parts, then call
  330.      * setenv to do all of the real work.
  331.      */
  332.  
  333.     value = strchr(string, '=');
  334.     if (value == NULL) {
  335.     return 0;
  336.     }
  337.     nameLength = value - string;
  338.     if (nameLength == 0) {
  339.     return 0;
  340.     }
  341.     name = ckalloc((unsigned) nameLength+1);
  342.     memcpy(name, string, nameLength);
  343.     name[nameLength] = 0;
  344.     setenv(name, value+1);
  345.     ckfree(name);
  346.     return 0;
  347. }
  348.  
  349. /*
  350.  *----------------------------------------------------------------------
  351.  *
  352.  * unsetenv --
  353.  *
  354.  *    Remove an environment variable, updating the "env" arrays
  355.  *    in all interpreters managed by us.
  356.  *
  357.  * Results:
  358.  *    None.
  359.  *
  360.  * Side effects:
  361.  *    Interpreters are updated, as is environ.
  362.  *
  363.  *----------------------------------------------------------------------
  364.  */
  365.  
  366. void
  367. unsetenv(name)
  368.     CONST char *name;            /* Name of variable to remove. */
  369. {
  370.     int index, dummy;
  371.     char **envPtr;
  372.     EnvInterp *eiPtr;
  373.  
  374.     if (environSize == 0) {
  375.     EnvInit();
  376.     }
  377.  
  378.     /*
  379.      * Update the environ array.
  380.      */
  381.  
  382.     index = FindVariable(name, &dummy);
  383.     if (index == -1) {
  384.     return;
  385.     }
  386.     ckfree(environ[index]);
  387.     for (envPtr = environ+index+1; ; envPtr++) {
  388.     envPtr[-1] = *envPtr;
  389.     if (*envPtr == NULL) {
  390.         break;
  391.        }
  392.     }
  393.  
  394.     /*
  395.      * Update all of the interpreters.
  396.      */
  397.  
  398.     for (eiPtr = firstInterpPtr; eiPtr != NULL; eiPtr = eiPtr->nextPtr) {
  399.     (void) Tcl_UnsetVar2(eiPtr->interp, "env", (char *) name,
  400.         TCL_GLOBAL_ONLY);
  401.     }
  402. }
  403.  
  404. /*
  405.  *----------------------------------------------------------------------
  406.  *
  407.  * EnvTraceProc --
  408.  *
  409.  *    This procedure is invoked whenever an environment variable
  410.  *    is modified or deleted.  It propagates the change to the
  411.  *    "environ" array and to any other interpreters for whom
  412.  *    we're managing an "env" array.
  413.  *
  414.  * Results:
  415.  *    Always returns NULL to indicate success.
  416.  *
  417.  * Side effects:
  418.  *    Environment variable changes get propagated.  If the whole
  419.  *    "env" array is deleted, then we stop managing things for
  420.  *    this interpreter (usually this happens because the whole
  421.  *    interpreter is being deleted).
  422.  *
  423.  *----------------------------------------------------------------------
  424.  */
  425.  
  426.     /* ARGSUSED */
  427. static char *
  428. EnvTraceProc(clientData, interp, name1, name2, flags)
  429.     ClientData clientData;    /* Not used. */
  430.     Tcl_Interp *interp;        /* Interpreter whose "env" variable is
  431.                  * being modified. */
  432.     char *name1;        /* Better be "env". */
  433.     char *name2;        /* Name of variable being modified, or
  434.                  * NULL if whole array is being deleted. */
  435.     int flags;            /* Indicates what's happening. */
  436. {
  437.     /*
  438.      * First see if the whole "env" variable is being deleted.  If
  439.      * so, just forget about this interpreter.
  440.      */
  441.  
  442.     if (name2 == NULL) {
  443.     register EnvInterp *eiPtr, *prevPtr;
  444.  
  445.     if ((flags & (TCL_TRACE_UNSETS|TCL_TRACE_DESTROYED))
  446.         != (TCL_TRACE_UNSETS|TCL_TRACE_DESTROYED)) {
  447.         panic("EnvTraceProc called with confusing arguments");
  448.     }
  449.     eiPtr = firstInterpPtr;
  450.     if (eiPtr->interp == interp) {
  451.         firstInterpPtr = eiPtr->nextPtr;
  452.     } else {
  453.         for (prevPtr = eiPtr, eiPtr = eiPtr->nextPtr; ;
  454.             prevPtr = eiPtr, eiPtr = eiPtr->nextPtr) {
  455.         if (eiPtr == NULL) {
  456.             panic("EnvTraceProc couldn't find interpreter");
  457.         }
  458.         if (eiPtr->interp == interp) {
  459.             prevPtr->nextPtr = eiPtr->nextPtr;
  460.             break;
  461.         }
  462.         }
  463.     }
  464.     ckfree((char *) eiPtr);
  465.     return NULL;
  466.     }
  467.  
  468.     /*
  469.      * If a value is being set, call setenv to do all of the work.
  470.      */
  471.  
  472.     if (flags & TCL_TRACE_WRITES) {
  473.     setenv(name2, Tcl_GetVar2(interp, "env", name2, TCL_GLOBAL_ONLY));
  474.     }
  475.  
  476.     if (flags & TCL_TRACE_UNSETS) {
  477.     unsetenv(name2);
  478.     }
  479.     return NULL;
  480. }
  481.  
  482. /*
  483.  *----------------------------------------------------------------------
  484.  *
  485.  * EnvInit --
  486.  *
  487.  *    This procedure is called to initialize our management
  488.  *    of the environ array.
  489.  *
  490.  * Results:
  491.  *    None.
  492.  *
  493.  * Side effects:
  494.  *    Environ gets copied to malloc-ed storage, so that in
  495.  *    the future we don't have to worry about which entries
  496.  *    are malloc-ed and which are static.
  497.  *
  498.  *----------------------------------------------------------------------
  499.  */
  500.  
  501. static void
  502. EnvInit()
  503. {
  504.     char **newEnviron;
  505.     int i, length;
  506.  
  507.     if (environSize != 0) {
  508.     return;
  509.     }
  510.     for (length = 0; environ[length] != NULL; length++) {
  511.     /* Empty loop body. */
  512.     }
  513.     environSize = length+5;
  514.     newEnviron = (char **) ckalloc((unsigned)
  515.         (environSize * sizeof(char *)));
  516.     for (i = 0; i < length; i++) {
  517.     newEnviron[i] = (char *) ckalloc((unsigned) (strlen(environ[i]) + 1));
  518.     strcpy(newEnviron[i], environ[i]);
  519.     }
  520.     newEnviron[length] = NULL;
  521.     environ = newEnviron;
  522. }
  523.