home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / crt / src / setenv.c < prev    next >
C/C++ Source or Header  |  1998-06-17  |  11KB  |  349 lines

  1. /***
  2. *setenv.c -set an environment variable in the environment
  3. *
  4. *       Copyright (c) 1993-1997, Microsoft Corporation. All rights reserved.
  5. *
  6. *Purpose:
  7. *       defines __crtsetenv() - adds a new variable to environment.
  8. *       Internal use only.
  9. *
  10. *******************************************************************************/
  11.  
  12.  
  13. #include <windows.h>
  14. #include <cruntime.h>
  15. #include <internal.h>
  16. #include <stdlib.h>
  17. #include <tchar.h>
  18. #include <rterr.h>
  19. #include <dbgint.h>
  20.  
  21. static _TSCHAR **copy_environ(_TSCHAR **);
  22.  
  23. #ifdef WPRFLAG
  24. static int __cdecl wfindenv(const wchar_t *name, int len);
  25. #else  /* WPRFLAG */
  26. static int __cdecl findenv(const char *name, int len);
  27. #endif  /* WPRFLAG */
  28.  
  29. /***
  30. *int __crtsetenv(option) - add/replace/remove variable in environment
  31. *
  32. *Purpose:
  33. *       option should be of the form "option=value".  If a string with the
  34. *       given option part already exists, it is replaced with the given
  35. *       string; otherwise the given string is added to the environment.
  36. *       If the string is of the form "option=", then the string is
  37. *       removed from the environment, if it exists.  If the string has
  38. *       no equals sign, error is returned.
  39. *
  40. *Entry:
  41. *       char *option - option string to set in the environment list.
  42. *           should be of the form "option=value".
  43. *       int primary - Only the primary call to _crt[w]setenv needs to
  44. *           create new copies or set the OS environment.
  45. *           1 indicates that this is the primary call.
  46. *
  47. *Exit:
  48. *       returns 0 if OK, -1 if fails.
  49. *
  50. *Exceptions:
  51. *
  52. *Warning:
  53. *       This code will not work if variables are removed from the
  54. *       environment by deleting them from environ[].  Use _putenv("option=")
  55. *       to remove a variable.
  56. *
  57. *******************************************************************************/
  58.  
  59. #ifdef WPRFLAG
  60. int __cdecl __crtwsetenv (
  61. #else  /* WPRFLAG */
  62. int __cdecl __crtsetenv (
  63. #endif  /* WPRFLAG */
  64.         const _TSCHAR *option,
  65.         const int primary
  66.         )
  67. {
  68.         int ix;
  69.         int remove; /* 1 if variable is to be removed */
  70.         _TSCHAR **env;
  71.         _TSCHAR *name, *value;
  72.         const _TSCHAR *equal;
  73.  
  74.         /*
  75.          * check that the option string is valid, find the equal sign
  76.          * and verify '=' is not the first character in string.
  77.          */
  78.         if ( (option == NULL) || ((equal = _tcschr(option, _T('='))) == NULL)
  79.             || option == equal)
  80.             return(-1);
  81.  
  82.         /* if the character following '=' is null, we are removing the
  83.          * the environment variable. Otherwise, we are adding or updating
  84.          * an environment variable.
  85.          */
  86.         remove = (*(equal + 1) == _T('\0'));
  87.  
  88.         /*
  89.          * the first time _[w]putenv() is called, copy the environment
  90.          * block that was passed to [w]main to avoid making a
  91.          * dangling pointer if the block is re-alloced.
  92.          */
  93. #ifdef WPRFLAG
  94.         if (_wenviron == __winitenv)
  95.             _wenviron = copy_environ(_wenviron);
  96. #else  /* WPRFLAG */
  97.         if (_environ == __initenv)
  98.             _environ = copy_environ(_environ);
  99. #endif  /* WPRFLAG */
  100.  
  101.         /* see if requested environment array exists */
  102.         if (_tenviron == NULL) {
  103.  
  104.             /*
  105.              * The requested type of environment does not exist.
  106.              * See if other type exists, if so convert it to requested type.
  107.              * The functions that convert the enviroment (__mbtow_environ and
  108.              * __wtomb_environ) will call this function (__crt[w]setenv) once
  109.              * for each of the pre-existing environment variables. To avoid
  110.              * an infinite loop, test the primary flag.
  111.              */
  112.  
  113. #ifdef WPRFLAG
  114.             if (primary && _environ)
  115.             {
  116.                 if (__mbtow_environ() != 0)
  117.                     return -1;
  118.             }
  119. #else  /* WPRFLAG */
  120.             if (primary && _wenviron)
  121.             {
  122.                 if (__wtomb_environ() != 0)
  123.                     return -1;
  124.             }
  125. #endif  /* WPRFLAG */
  126.             else {
  127.                 /* nothing to remove, return */
  128.                 if ( remove )
  129.                     return 0;
  130.                 else {
  131.                     /* create ones that do not exist */
  132.  
  133.                     if (_environ == NULL)
  134.                     {
  135.                         if ( (_environ = _malloc_crt(sizeof(char *))) == NULL)
  136.                             return -1;
  137.                         *_environ = NULL;
  138.                     }
  139.  
  140.                     if (_wenviron == NULL)
  141.                     {
  142.                         if ( (_wenviron = _malloc_crt(sizeof(wchar_t *))) == NULL)
  143.                             return -1;
  144.                         *_wenviron = NULL;
  145.                     }
  146.                 }
  147.             }
  148.         }
  149.  
  150.         /*
  151.          * At this point, the two types of environments are in sync (as much
  152.          * as they can be anyway). The only way they can get out of sync
  153.          * (besides users directly modifiying the environment) is if there
  154.          * are conversion problems: If the user sets two Unicode EVs,
  155.          * "foo1" and "foo2" and converting then to multibyte yields "foo?"
  156.          * and "foo?", then the environment blocks will differ.
  157.          */
  158.  
  159.         /* init env pointers */
  160.         env = _tenviron;
  161.  
  162.         /* See if the string is already in the environment */
  163. #ifdef WPRFLAG
  164.         ix = wfindenv(option, equal - option);
  165. #else  /* WPRFLAG */
  166.         ix = findenv(option, equal - option);
  167. #endif  /* WPRFLAG */
  168.  
  169.         if ((ix >= 0) && (*env != NULL)) {
  170.             /* String is already in the environment - overwrite/remove it */
  171.             if (remove) {
  172.  
  173.                 /* free the string being removed */
  174.                 _free_crt(env[ix]);
  175.  
  176.                 /* removing -- move all the later strings up */
  177.                 for ( ; env[ix] != NULL; ++ix) {
  178.                     env[ix] = env[ix+1];
  179.                 }
  180.  
  181.                 /* shrink the environment memory block
  182.                    (ix now has number of strings, including NULL) --
  183.                    this realloc probably can't fail, since we're
  184.                    shrinking a mem block, but we're careful anyway. */
  185.                 if (env = (_TSCHAR **) _realloc_crt(env, ix * sizeof(_TSCHAR *)))
  186.                     _tenviron = env;
  187.             }
  188.             else {
  189.                 /* replace the option */
  190.                 env[ix] = (_TSCHAR *) option;
  191.             }
  192.         }
  193.         else {
  194.             /*
  195.              * String is NOT in the environment
  196.              */
  197.             if ( !remove )  {
  198.                 /*
  199.                  * Append the string to the environ table. Note that
  200.                  * table must be grown to do this.
  201.                  */
  202.                 if (ix < 0)
  203.                     ix = -ix;    /* ix = length of environ table */
  204.  
  205.                 if ( (env = (_TSCHAR **)_realloc_crt(env, sizeof(_TSCHAR *) *
  206.                     (ix + 2))) == NULL )
  207.                     return -1;
  208.  
  209.                 env[ix] = (_TSCHAR *)option;
  210.                 env[ix + 1] = NULL;
  211.  
  212.                 _tenviron = env;
  213.             }
  214.             else
  215.                 /*
  216.                  * We are asked to remove an environment var that
  217.                  * isn't there...just return success
  218.                  */
  219.                 return 0;
  220.         }
  221.  
  222.         /*
  223.          * Update the OS environment. Don't give an error if this fails
  224.          * since the failure will not affect the user unless he/she is making
  225.          * direct API calls. Only need to do this for one type, OS converts
  226.          * to other type automatically.
  227.          */
  228.         if ( primary &&
  229.             (name = (_TSCHAR *)_malloc_crt((_tcslen(option) + 2) * sizeof(_TSCHAR))) != NULL )
  230.         {
  231.             _tcscpy(name, option);
  232.             value = name + (equal - option);
  233.             *value++ = _T('\0');
  234.             SetEnvironmentVariable(name, remove ? NULL : value);
  235.             _free_crt(name);
  236.         }
  237.  
  238.         return 0;
  239. }
  240.  
  241.  
  242. /***
  243. *int findenv(name, len) - [STATIC]
  244. *
  245. *Purpose:
  246. *       Scan for the given string within the environment
  247. *
  248. *Entry:
  249. *
  250. *Exit:
  251. *       Returns the offset in "environ[]" of the given variable
  252. *       Returns the negative of the length of environ[] if not found.
  253. *       Returns 0 if the environment is empty.
  254. *
  255. *       [NOTE: That a 0 return can mean that the environment is empty
  256. *       or that the string was found as the first entry in the array.]
  257. *
  258. *Exceptions:
  259. *
  260. *******************************************************************************/
  261.  
  262. #ifdef WPRFLAG
  263. static int __cdecl wfindenv (
  264. #else  /* WPRFLAG */
  265. static int __cdecl findenv (
  266. #endif  /* WPRFLAG */
  267.         const _TSCHAR *name,
  268.         int len
  269.         )
  270. {
  271.         _TSCHAR **env;
  272.  
  273.         for ( env = _tenviron ; *env != NULL ; env++ ) {
  274.             /*
  275.              * See if first len characters match, up to case
  276.              */
  277.             if ( _tcsnicoll(name, *env, len) == 0 )
  278.                 /*
  279.                  * the next character of the environment string must
  280.                  * be an '=' or a '\0'
  281.                  */
  282.                 if ( (*env)[len] == _T('=') || (*env)[len] == _T('\0') )
  283.                     return(env - _tenviron);
  284. //
  285. // We cannot break here since findenv must report the total number of strings.
  286. //              else
  287. //                  break;
  288.         }
  289.  
  290.         return(-(env - _tenviron));
  291. }
  292.  
  293.  
  294. /***
  295. *copy_environ - copy an environment block
  296. *
  297. *Purpose:
  298. *       Create a copy of an environment block.
  299. *
  300. *Entry:
  301. *       _TSCHAR **oldenviron - pointer to enviroment to be copied.
  302. *
  303. *Exit:
  304. *       Returns a pointer to newly created environment.
  305. *
  306. *Exceptions:
  307. *
  308. *******************************************************************************/
  309.  
  310. static _TSCHAR **copy_environ(_TSCHAR **oldenviron)
  311. {
  312.         int cvars = 0;
  313.         _TSCHAR **oldenvptr = oldenviron;
  314.         _TSCHAR **newenviron, **newenvptr;
  315.  
  316.         /* no environment */
  317.         if (oldenviron == NULL)
  318.             return NULL;
  319.  
  320.         /* count number of environment variables */
  321.         while (*oldenvptr++)
  322.             cvars++;
  323.  
  324.         /* need pointer for each string, plus one null ptr at end */
  325.         if ( (newenviron = newenvptr = (_TSCHAR **)
  326.             _malloc_crt((cvars+1) * sizeof(_TSCHAR *))) == NULL )
  327.             _amsg_exit(_RT_SPACEENV);
  328.  
  329.         /* duplicate the environment variable strings */
  330.         oldenvptr = oldenviron;
  331.         while (*oldenvptr)
  332. #ifdef _DEBUG
  333.         {
  334.             if ( (*newenvptr = _malloc_crt((_tcslen(*oldenvptr)+1)
  335.                   * sizeof(_TSCHAR))) != NULL )
  336.                 _tcscpy(*newenvptr, *oldenvptr);
  337.             oldenvptr++;
  338.             newenvptr++;
  339.         }
  340. #else  /* _DEBUG */
  341.             *newenvptr++ = _tcsdup(*oldenvptr++);
  342. #endif  /* _DEBUG */
  343.  
  344.         *newenvptr = NULL;
  345.  
  346.         return newenviron;
  347. }
  348.  
  349.