Changeset 95792 in vbox for trunk/src/VBox/Runtime/r3/win/env-win.cpp
- Timestamp:
- Jul 25, 2022 9:01:44 AM (2 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/r3/win/env-win.cpp
r93115 r95792 31 31 #include <iprt/env.h> 32 32 33 #ifdef IPRT_NO_CRT 34 # include <iprt/asm.h> 35 #endif 33 36 #include <iprt/alloca.h> 34 37 #include <iprt/assert.h> … … 38 41 #include <iprt/utf16.h> 39 42 40 #include <stdlib.h> 41 #include <errno.h> 43 #ifndef IPRT_NO_CRT 44 # include <stdlib.h> 45 # include <errno.h> 46 #endif 47 #include <iprt/win/windows.h> 48 49 50 /********************************************************************************************************************************* 51 * Global Variables * 52 *********************************************************************************************************************************/ 53 #ifdef IPRT_NO_CRT 54 static uint32_t volatile g_idxGetEnvBufs = 0; 55 static char *g_apszGetEnvBufs[64]; /* leak */ 56 #endif 57 58 59 /********************************************************************************************************************************* 60 * Internal Functions * 61 *********************************************************************************************************************************/ 62 int rtEnvSetUtf8Worker(const char *pchVar, size_t cchVar, const char *pszValue); 63 42 64 43 65 44 66 RTDECL(bool) RTEnvExistsBad(const char *pszVar) 45 67 { 68 #ifndef IPRT_NO_CRT 46 69 return RTEnvGetBad(pszVar) != NULL; 70 #else 71 return RTEnvExistsUtf8(pszVar); 72 #endif 47 73 } 48 74 … … 50 76 RTDECL(bool) RTEnvExist(const char *pszVar) 51 77 { 78 #ifndef IPRT_NO_CRT 52 79 return RTEnvExistsBad(pszVar); 80 #else 81 return RTEnvExistsUtf8(pszVar); 82 #endif 53 83 } 54 84 … … 61 91 int rc = RTStrToUtf16(pszVar, &pwszVar); 62 92 AssertRCReturn(rc, false); 93 94 #ifndef IPRT_NO_CRT 63 95 bool fRet = _wgetenv(pwszVar) != NULL; 96 #else 97 DWORD dwRet = GetEnvironmentVariableW(pwszVar, NULL, 0); 98 bool fRet = dwRet != 0; 99 #endif 100 64 101 RTUtf16Free(pwszVar); 65 102 return fRet; … … 70 107 { 71 108 AssertReturn(strchr(pszVar, '=') == NULL, NULL); 109 #ifndef IPRT_NO_CRT 72 110 return getenv(pszVar); 111 #else 112 /* 113 * Query the value into heap buffer which we give a lifetime of 64 114 * RTEnvGetBad calls. 115 */ 116 char *pszValue = RTEnvDup(pszVar); 117 if (pszValue) 118 { 119 RTMEM_MAY_LEAK(pszValue); /* Quite possible we'll leak this, but the leak is limited to 64 values. */ 120 121 uint32_t idx = ASMAtomicIncU32(&g_idxGetEnvBufs) % RT_ELEMENTS(g_apszGetEnvBufs); 122 char *pszOld = (char *)ASMAtomicXchgPtr((void * volatile *)&g_apszGetEnvBufs[idx], pszValue); 123 RTStrFree(pszOld); 124 } 125 return pszValue; 126 #endif 73 127 } 74 128 … … 91 145 *pcchActual = 0; 92 146 147 /* 148 * Convert the name to UTF-16. 149 */ 93 150 PRTUTF16 pwszVar; 94 151 int rc = RTStrToUtf16(pszVar, &pwszVar); 95 152 AssertRCReturn(rc, rc); 96 153 97 /** @todo Consider _wgetenv_s or GetEnvironmentVariableW here to avoid the 98 * potential race with a concurrent _wputenv/_putenv. */ 99 PCRTUTF16 pwszValue = _wgetenv(pwszVar); 154 /* 155 * Query the variable. First try with a medium sized stack buffer (too 156 * small for your typical PATH, but large enough for most other things). 157 */ 158 RTUTF16 wszValue[512]; 159 uint32_t cwcValueBuf = RT_ELEMENTS(wszValue); 160 PRTUTF16 pwszValue = wszValue; 161 PRTUTF16 pwszValueFree = NULL; 162 163 for (unsigned iTry = 0;; iTry++) 164 { 165 /* This API is weird, it didn't always set ERROR_BUFFER_OVERFLOW. 166 Note! Assume that the CRT transparently updates the process 167 environment and that we don't need to use _wgetenv_s here. */ 168 SetLastError(NO_ERROR); 169 DWORD const cwcValueRet = GetEnvironmentVariableW(pwszVar, pwszValue, cwcValueBuf); 170 DWORD const dwErr = GetLastError(); 171 172 if (cwcValueRet < cwcValueBuf) 173 { 174 if (cwcValueRet > 0 || dwErr == NO_ERROR) /* In case of empty values we have to see if last error was set or not. */ 175 { 176 if (cbValue) 177 rc = RTUtf16ToUtf8Ex(pwszValue, cwcValueRet, &pszValue, cbValue, pcchActual); 178 else 179 rc = RTUtf16CalcUtf8LenEx(pwszValue, cwcValueRet, pcchActual); 180 } 181 else 182 { 183 Assert(cwcValueRet == 0); 184 Assert(dwErr != NO_ERROR); 185 rc = RTErrConvertFromWin32(dwErr); 186 } 187 break; 188 } 189 190 /* 191 * Insufficient buffer, so increase it. The first re-try will use the 192 * returned size, further re-tries will max out with a multiple of the 193 * stack buffer till we reaches 32KB chars (128 loops). 194 */ 195 Assert(dwErr == NO_ERROR || dwErr == ERROR_BUFFER_OVERFLOW); 196 RTMemTmpFree(pwszValueFree); 197 AssertBreakStmt(cwcValueBuf < _32K, rc = VERR_INTERNAL_ERROR_3 /* not a good one */); 198 199 cwcValueBuf = RT_MAX(cwcValueRet + iTry, RT_ELEMENTS(wszValue) * iTry); 200 pwszValueFree = pwszValue = (PRTUTF16)RTMemTmpAlloc(cwcValueBuf * sizeof(RTUTF16)); 201 AssertBreakStmt(pwszValue, rc = VERR_NO_TMP_MEMORY); 202 } 203 204 RTMemTmpFree(pwszValueFree); 100 205 RTUtf16Free(pwszVar); 101 if (pwszValue) 102 { 103 if (cbValue) 104 rc = RTUtf16ToUtf8Ex(pwszValue, RTSTR_MAX, &pszValue, cbValue, pcchActual); 105 else 106 rc = RTUtf16CalcUtf8LenEx(pwszValue, RTSTR_MAX, pcchActual); 107 } 108 else 109 rc = VERR_ENV_VAR_NOT_FOUND; 110 return rc; 206 return rc; 207 } 208 209 210 RTDECL(char *) RTEnvDup(const char *pszVar) 211 { 212 AssertPtrReturn(pszVar, NULL); 213 214 /* 215 * Convert the name to UTF-16. 216 */ 217 PRTUTF16 pwszVar; 218 int rc = RTStrToUtf16(pszVar, &pwszVar); 219 AssertRCReturn(rc, NULL); 220 221 /* 222 * Query the variable. First try with a medium sized stack buffer (too 223 * small for your typical PATH, but large enough for most other things). 224 */ 225 char *pszRet = NULL; 226 RTUTF16 wszValue[512]; 227 uint32_t cwcValueBuf = RT_ELEMENTS(wszValue); 228 PRTUTF16 pwszValue = wszValue; 229 PRTUTF16 pwszValueFree = NULL; 230 231 for (unsigned iTry = 0;; iTry++) 232 { 233 /* This API is weird, it didn't always set ERROR_BUFFER_OVERFLOW. 234 Note! Assume that the CRT transparently updates the process 235 environment and that we don't need to use _wgetenv_s here. */ 236 SetLastError(NO_ERROR); 237 DWORD const cwcValueRet = GetEnvironmentVariableW(pwszVar, pwszValue, cwcValueBuf); 238 DWORD const dwErr = GetLastError(); 239 240 if (cwcValueRet < cwcValueBuf) 241 { 242 if (cwcValueRet > 0 || dwErr == NO_ERROR) /* In case of empty values we have to see if last error was set or not. */ 243 { 244 rc = RTUtf16ToUtf8Ex(pwszValue, cwcValueRet, &pszRet, 0, NULL); 245 if (RT_FAILURE(rc)) 246 pszRet = NULL; 247 } 248 else 249 { 250 Assert(cwcValueRet == 0); 251 Assert(dwErr != NO_ERROR); 252 } 253 break; 254 } 255 256 /* 257 * Insufficient buffer, so increase it. The first re-try will use the 258 * returned size, further re-tries will max out with a multiple of the 259 * stack buffer till we reaches 32KB chars (128 loops). 260 */ 261 Assert(dwErr == NO_ERROR || dwErr == ERROR_BUFFER_OVERFLOW); 262 RTMemTmpFree(pwszValueFree); 263 AssertBreakStmt(cwcValueBuf < _32K, rc = VERR_INTERNAL_ERROR_3 /* not a good one */); 264 265 cwcValueBuf = RT_MAX(cwcValueRet + iTry, RT_ELEMENTS(wszValue) * iTry); 266 pwszValueFree = pwszValue = (PRTUTF16)RTMemTmpAlloc(cwcValueBuf * sizeof(RTUTF16)); 267 AssertBreakStmt(pwszValue, rc = VERR_NO_TMP_MEMORY); 268 } 269 270 RTMemTmpFree(pwszValueFree); 271 RTUtf16Free(pwszVar); 272 return pszRet; 111 273 } 112 274 … … 114 276 RTDECL(int) RTEnvPutBad(const char *pszVarEqualValue) 115 277 { 278 #ifndef IPRT_NO_CRT 116 279 /** @todo putenv is a source memory leaks. deal with this on a per system basis. */ 117 280 if (!putenv((char *)pszVarEqualValue)) 118 281 return 0; 119 282 return RTErrConvertFromErrno(errno); 283 #else 284 return RTEnvPutUtf8(pszVarEqualValue); 285 #endif 120 286 } 121 287 … … 123 289 RTDECL(int) RTEnvPut(const char *pszVarEqualValue) 124 290 { 291 #ifndef IPRT_NO_CRT 125 292 return RTEnvPutBad(pszVarEqualValue); 293 #else 294 return RTEnvPutUtf8(pszVarEqualValue); 295 #endif 126 296 } 127 297 … … 133 303 if (RT_SUCCESS(rc)) 134 304 { 305 #ifndef IPRT_NO_CRT 135 306 if (!_wputenv(pwszVarEqualValue)) 136 307 rc = VINF_SUCCESS; 137 308 else 138 309 rc = RTErrConvertFromErrno(errno); 310 #else 311 PRTUTF16 pwszValue = RTUtf16Chr(pwszVarEqualValue, '='); 312 if (pwszValue) 313 { 314 *pwszValue++ = '\0'; 315 316 SetLastError(*pwszValue ? ERROR_OUTOFMEMORY : ERROR_ENVVAR_NOT_FOUND); /* The API did not always set the last error. */ 317 if (SetEnvironmentVariableW(pwszVarEqualValue, *pwszValue ? pwszValue : NULL)) 318 rc = VINF_SUCCESS; 319 else 320 { 321 DWORD dwErr = GetLastError(); 322 if (dwErr == ERROR_ENVVAR_NOT_FOUND) 323 { 324 Assert(!*pwszValue); 325 rc = VINF_SUCCESS; 326 } 327 else 328 { 329 Assert(*pwszValue); 330 rc = RTErrConvertFromWin32(GetLastError()); 331 } 332 } 333 } 334 else 335 rc = VERR_INVALID_PARAMETER; 336 #endif 139 337 RTUtf16Free(pwszVarEqualValue); 140 338 } … … 146 344 RTDECL(int) RTEnvSetBad(const char *pszVar, const char *pszValue) 147 345 { 346 #ifndef IPRT_NO_CRT 148 347 AssertMsgReturn(strchr(pszVar, '=') == NULL, ("'%s'\n", pszVar), VERR_ENV_INVALID_VAR_NAME); 149 150 /* make a local copy and feed it to putenv. */ 151 const size_t cchVar = strlen(pszVar); 152 const size_t cchValue = strlen(pszValue); 153 char *pszTmp = (char *)alloca(cchVar + cchValue + 2 + !*pszValue); 154 memcpy(pszTmp, pszVar, cchVar); 155 pszTmp[cchVar] = '='; 156 if (*pszValue) 157 memcpy(pszTmp + cchVar + 1, pszValue, cchValue + 1); 348 int rc; 349 if (!RTEnvExist(pszVar)) 350 rc = VINF_ENV_VAR_NOT_FOUND; 158 351 else 159 352 { 160 pszTmp[cchVar + 1] = ' '; /* wrong, but putenv will remove it otherwise. */ 161 pszTmp[cchVar + 2] = '\0'; 162 } 163 164 if (!putenv(pszTmp)) 165 return 0; 166 return RTErrConvertFromErrno(errno); 353 errno_t rcErrno = _putenv_s(pszVar, *pszValue ? pszValue : " " /* wrong, but will be treated as unset otherwise */); 354 if (rcErrno == 0) 355 rc = VINF_SUCCESS; 356 else 357 rc = RTErrConvertFromErrno(rcErrno); 358 } 359 return rc; 360 #else 361 return RTEnvSetUtf8(pszVar, pszValue); 362 #endif 167 363 } 168 364 … … 170 366 RTDECL(int) RTEnvSet(const char *pszVar, const char *pszValue) 171 367 { 368 #ifndef IPRT_NO_CRT 172 369 return RTEnvSetBad(pszVar, pszValue); 370 #else 371 return RTEnvSetUtf8(pszVar, pszValue); 372 #endif 173 373 } 174 374 … … 179 379 int rtEnvSetUtf8Worker(const char *pchVar, size_t cchVar, const char *pszValue) 180 380 { 181 size_t cwcVar;182 int rc = RTStr CalcUtf16LenEx(pchVar, cchVar, &cwcVar);381 PRTUTF16 pwszVar; 382 int rc = RTStrToUtf16Ex(pchVar, cchVar, &pwszVar, 0, NULL); 183 383 if (RT_SUCCESS(rc)) 184 384 { 185 size_t cwcValue;186 rc = RTStr CalcUtf16LenEx(pszValue, RTSTR_MAX, &cwcValue);385 PRTUTF16 pwszValue; 386 rc = RTStrToUtf16(pszValue, &pwszValue); 187 387 if (RT_SUCCESS(rc)) 188 388 { 189 PRTUTF16 pwszTmp = (PRTUTF16)RTMemTmpAlloc((cwcVar + 1 + cwcValue + 1) * sizeof(RTUTF16)); 190 if (pwszTmp) 191 { 192 rc = RTStrToUtf16Ex(pchVar, cchVar, &pwszTmp, cwcVar + 1, NULL); 193 if (RT_SUCCESS(rc)) 194 { 195 PRTUTF16 pwszTmpValue = &pwszTmp[cwcVar]; 196 *pwszTmpValue++ = '='; 197 rc = RTStrToUtf16Ex(pszValue, RTSTR_MAX, &pwszTmpValue, cwcValue + 1, NULL); 198 if (RT_SUCCESS(rc)) 199 { 200 if (!_wputenv(pwszTmp)) 201 rc = VINF_SUCCESS; 202 else 203 rc = RTErrConvertFromErrno(errno); 204 } 205 } 206 RTMemTmpFree(pwszTmp); 207 } 208 else 209 rc = VERR_NO_TMP_MEMORY; 210 } 389 #ifndef IPRT_NO_CRT 390 errno_t rcErrno = _wputenv_s(pwszVar, 391 *pwszValue ? pwszValue : L" " /* wrong, but will be treated as unset otherwise */); 392 if (rcErrno == 0) 393 rc = VINF_SUCCESS; 394 else 395 rc = RTErrConvertFromErrno(rcErrno); 396 #else 397 SetLastError(ERROR_OUTOFMEMORY); /* The API did not always set the last error. */ 398 if (SetEnvironmentVariableW(pwszVar, pwszValue)) 399 rc = VINF_SUCCESS; 400 else 401 rc = RTErrConvertFromWin32(GetLastError()); 402 #endif 403 RTUtf16Free(pwszValue); 404 } 405 RTUtf16Free(pwszVar); 211 406 } 212 407 return rc; … … 224 419 RTDECL(int) RTEnvUnsetBad(const char *pszVar) 225 420 { 421 #ifndef IPRT_NO_CRT 226 422 AssertReturn(strchr(pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME); 227 228 /* 229 * Check that it exists first. 230 */ 423 int rc; 231 424 if (!RTEnvExist(pszVar)) 232 return VINF_ENV_VAR_NOT_FOUND; 233 234 /* 235 * Ok, try remove it. 236 */ 237 #ifdef RT_OS_WINDOWS 238 /* Use putenv(var=) since Windows does not have unsetenv(). */ 239 size_t cchVar = strlen(pszVar); 240 char *pszBuf = (char *)alloca(cchVar + 2); 241 memcpy(pszBuf, pszVar, cchVar); 242 pszBuf[cchVar] = '='; 243 pszBuf[cchVar + 1] = '\0'; 244 245 if (!putenv(pszBuf)) 246 return VINF_SUCCESS; 247 248 #else 249 /* This is the preferred function as putenv() like used above does neither work on Solaris nor on Darwin. */ 250 if (!unsetenv((char*)pszVar)) 251 return VINF_SUCCESS; 252 #endif 253 254 return RTErrConvertFromErrno(errno); 425 rc = VINF_ENV_VAR_NOT_FOUND; 426 else 427 { 428 errno_t rcErrno = _putenv_s(pszVar, NULL); 429 if (rcErrno == 0) 430 rc = VINF_SUCCESS; 431 else 432 rc = RTErrConvertFromErrno(rcErrno); 433 } 434 return rc; 435 #else 436 return RTEnvUnsetUtf8(pszVar); 437 #endif 255 438 } 256 439 … … 258 441 RTDECL(int) RTEnvUnset(const char *pszVar) 259 442 { 443 #ifndef IPRT_NO_CRT 260 444 return RTEnvUnsetBad(pszVar); 445 #else 446 return RTEnvUnsetUtf8(pszVar); 447 #endif 261 448 } 262 449 … … 266 453 AssertReturn(strchr(pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME); 267 454 268 size_t cwcVar;269 int rc = RTStr CalcUtf16LenEx(pszVar, RTSTR_MAX, &cwcVar);455 PRTUTF16 pwszVar; 456 int rc = RTStrToUtf16(pszVar, &pwszVar); 270 457 if (RT_SUCCESS(rc)) 271 458 { 272 PRTUTF16 pwszTmp = (PRTUTF16)RTMemTmpAlloc((cwcVar + 1 + 1) * sizeof(RTUTF16)); 273 if (pwszTmp) 274 { 275 rc = RTStrToUtf16Ex(pszVar, RTSTR_MAX, &pwszTmp, cwcVar + 1, NULL); 276 if (RT_SUCCESS(rc)) 277 { 278 pwszTmp[cwcVar] = '='; 279 pwszTmp[cwcVar + 1] = '\0'; 280 if (!_wputenv(pwszTmp)) 281 rc = VINF_SUCCESS; 282 else 283 rc = RTErrConvertFromErrno(errno); 284 } 285 RTMemTmpFree(pwszTmp); 286 } 287 } 288 return rc; 289 } 290 459 #ifndef IPRT_NO_CRT 460 if (_wgetenv(pwszVar)) 461 { 462 errno_t rcErrno = _wputenv_s(pwszVar, NULL); 463 if (rcErrno == 0) 464 rc = VINF_SUCCESS; 465 else 466 rc = RTErrConvertFromErrno(rcErrno); 467 } 468 else 469 rc = VINF_ENV_VAR_NOT_FOUND; 470 #else 471 SetLastError(ERROR_ENVVAR_NOT_FOUND); /* The API did not always set the last error. */ 472 if (SetEnvironmentVariableW(pwszVar, NULL)) 473 rc = VINF_SUCCESS; 474 else 475 { 476 DWORD dwErr = GetLastError(); 477 rc = dwErr == ERROR_ENVVAR_NOT_FOUND ? VINF_ENV_VAR_NOT_FOUND : RTErrConvertFromWin32(dwErr); 478 } 479 #endif 480 RTUtf16Free(pwszVar); 481 } 482 return rc; 483 } 484
Note:
See TracChangeset
for help on using the changeset viewer.