Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

433 lines
14 KiB

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