mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 19:43:32 +00:00
501 lines
14 KiB
C++
501 lines
14 KiB
C++
|
/*
|
||
|
* kmp_environment.cpp -- Handle environment variables OS-independently.
|
||
|
*/
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
//
|
||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
/* We use GetEnvironmentVariable for Windows* OS instead of getenv because the
|
||
|
act of loading a DLL on Windows* OS makes any user-set environment variables
|
||
|
(i.e. with putenv()) unavailable. getenv() apparently gets a clean copy of
|
||
|
the env variables as they existed at the start of the run. JH 12/23/2002
|
||
|
|
||
|
On Windows* OS, there are two environments (at least, see below):
|
||
|
|
||
|
1. Environment maintained by Windows* OS on IA-32 architecture. Accessible
|
||
|
through GetEnvironmentVariable(), SetEnvironmentVariable(), and
|
||
|
GetEnvironmentStrings().
|
||
|
|
||
|
2. Environment maintained by C RTL. Accessible through getenv(), putenv().
|
||
|
|
||
|
putenv() function updates both C and Windows* OS on IA-32 architecture.
|
||
|
getenv() function search for variables in C RTL environment only.
|
||
|
Windows* OS on IA-32 architecture functions work *only* with Windows* OS on
|
||
|
IA-32 architecture.
|
||
|
|
||
|
Windows* OS on IA-32 architecture maintained by OS, so there is always only
|
||
|
one Windows* OS on IA-32 architecture per process. Changes in Windows* OS on
|
||
|
IA-32 architecture are process-visible.
|
||
|
|
||
|
C environment maintained by C RTL. Multiple copies of C RTL may be present
|
||
|
in the process, and each C RTL maintains its own environment. :-(
|
||
|
|
||
|
Thus, proper way to work with environment on Windows* OS is:
|
||
|
|
||
|
1. Set variables with putenv() function -- both C and Windows* OS on IA-32
|
||
|
architecture are being updated. Windows* OS on IA-32 architecture may be
|
||
|
considered primary target, while updating C RTL environment is free bonus.
|
||
|
|
||
|
2. Get variables with GetEnvironmentVariable() -- getenv() does not
|
||
|
search Windows* OS on IA-32 architecture, and can not see variables
|
||
|
set with SetEnvironmentVariable().
|
||
|
|
||
|
2007-04-05 -- lev
|
||
|
*/
|
||
|
|
||
|
#include "kmp_environment.h"
|
||
|
|
||
|
#include "kmp.h" //
|
||
|
#include "kmp_i18n.h"
|
||
|
#include "kmp_os.h" // KMP_OS_*.
|
||
|
#include "kmp_str.h" // __kmp_str_*().
|
||
|
|
||
|
#if KMP_OS_UNIX
|
||
|
#include <stdlib.h> // getenv, setenv, unsetenv.
|
||
|
#include <string.h> // strlen, strcpy.
|
||
|
#if KMP_OS_DARWIN
|
||
|
#include <crt_externs.h>
|
||
|
#define environ (*_NSGetEnviron())
|
||
|
#else
|
||
|
extern char **environ;
|
||
|
#endif
|
||
|
#elif KMP_OS_WINDOWS
|
||
|
#include <windows.h> // GetEnvironmentVariable, SetEnvironmentVariable,
|
||
|
// GetLastError.
|
||
|
#else
|
||
|
#error Unknown or unsupported OS.
|
||
|
#endif
|
||
|
|
||
|
// TODO: Eliminate direct memory allocations, use string operations instead.
|
||
|
|
||
|
static inline void *allocate(size_t size) {
|
||
|
void *ptr = KMP_INTERNAL_MALLOC(size);
|
||
|
if (ptr == NULL) {
|
||
|
KMP_FATAL(MemoryAllocFailed);
|
||
|
}
|
||
|
return ptr;
|
||
|
} // allocate
|
||
|
|
||
|
char *__kmp_env_get(char const *name) {
|
||
|
|
||
|
char *result = NULL;
|
||
|
|
||
|
#if KMP_OS_UNIX
|
||
|
char const *value = getenv(name);
|
||
|
if (value != NULL) {
|
||
|
size_t len = KMP_STRLEN(value) + 1;
|
||
|
result = (char *)KMP_INTERNAL_MALLOC(len);
|
||
|
if (result == NULL) {
|
||
|
KMP_FATAL(MemoryAllocFailed);
|
||
|
}
|
||
|
KMP_STRNCPY_S(result, len, value, len);
|
||
|
}
|
||
|
#elif KMP_OS_WINDOWS
|
||
|
/* We use GetEnvironmentVariable for Windows* OS instead of getenv because the
|
||
|
act of loading a DLL on Windows* OS makes any user-set environment
|
||
|
variables (i.e. with putenv()) unavailable. getenv() apparently gets a
|
||
|
clean copy of the env variables as they existed at the start of the run.
|
||
|
JH 12/23/2002 */
|
||
|
DWORD rc;
|
||
|
rc = GetEnvironmentVariable(name, NULL, 0);
|
||
|
if (!rc) {
|
||
|
DWORD error = GetLastError();
|
||
|
if (error != ERROR_ENVVAR_NOT_FOUND) {
|
||
|
__kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error), __kmp_msg_null);
|
||
|
}
|
||
|
// Variable is not found, it's ok, just continue.
|
||
|
} else {
|
||
|
DWORD len = rc;
|
||
|
result = (char *)KMP_INTERNAL_MALLOC(len);
|
||
|
if (result == NULL) {
|
||
|
KMP_FATAL(MemoryAllocFailed);
|
||
|
}
|
||
|
rc = GetEnvironmentVariable(name, result, len);
|
||
|
if (!rc) {
|
||
|
// GetEnvironmentVariable() may return 0 if variable is empty.
|
||
|
// In such a case GetLastError() returns ERROR_SUCCESS.
|
||
|
DWORD error = GetLastError();
|
||
|
if (error != ERROR_SUCCESS) {
|
||
|
// Unexpected error. The variable should be in the environment,
|
||
|
// and buffer should be large enough.
|
||
|
__kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error),
|
||
|
__kmp_msg_null);
|
||
|
KMP_INTERNAL_FREE((void *)result);
|
||
|
result = NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
#error Unknown or unsupported OS.
|
||
|
#endif
|
||
|
|
||
|
return result;
|
||
|
|
||
|
} // func __kmp_env_get
|
||
|
|
||
|
// TODO: Find and replace all regular free() with __kmp_env_free().
|
||
|
|
||
|
void __kmp_env_free(char const **value) {
|
||
|
|
||
|
KMP_DEBUG_ASSERT(value != NULL);
|
||
|
KMP_INTERNAL_FREE(CCAST(char *, *value));
|
||
|
*value = NULL;
|
||
|
|
||
|
} // func __kmp_env_free
|
||
|
|
||
|
int __kmp_env_exists(char const *name) {
|
||
|
|
||
|
#if KMP_OS_UNIX
|
||
|
char const *value = getenv(name);
|
||
|
return ((value == NULL) ? (0) : (1));
|
||
|
#elif KMP_OS_WINDOWS
|
||
|
DWORD rc;
|
||
|
rc = GetEnvironmentVariable(name, NULL, 0);
|
||
|
if (rc == 0) {
|
||
|
DWORD error = GetLastError();
|
||
|
if (error != ERROR_ENVVAR_NOT_FOUND) {
|
||
|
__kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error), __kmp_msg_null);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
return 1;
|
||
|
#else
|
||
|
#error Unknown or unsupported OS.
|
||
|
#endif
|
||
|
|
||
|
} // func __kmp_env_exists
|
||
|
|
||
|
void __kmp_env_set(char const *name, char const *value, int overwrite) {
|
||
|
|
||
|
#if KMP_OS_UNIX
|
||
|
int rc = setenv(name, value, overwrite);
|
||
|
if (rc != 0) {
|
||
|
// Dead code. I tried to put too many variables into Linux* OS
|
||
|
// environment on IA-32 architecture. When application consumes
|
||
|
// more than ~2.5 GB of memory, entire system feels bad. Sometimes
|
||
|
// application is killed (by OS?), sometimes system stops
|
||
|
// responding... But this error message never appears. --ln
|
||
|
__kmp_fatal(KMP_MSG(CantSetEnvVar, name), KMP_HNT(NotEnoughMemory),
|
||
|
__kmp_msg_null);
|
||
|
}
|
||
|
#elif KMP_OS_WINDOWS
|
||
|
BOOL rc;
|
||
|
if (!overwrite) {
|
||
|
rc = GetEnvironmentVariable(name, NULL, 0);
|
||
|
if (rc) {
|
||
|
// Variable exists, do not overwrite.
|
||
|
return;
|
||
|
}
|
||
|
DWORD error = GetLastError();
|
||
|
if (error != ERROR_ENVVAR_NOT_FOUND) {
|
||
|
__kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error), __kmp_msg_null);
|
||
|
}
|
||
|
}
|
||
|
rc = SetEnvironmentVariable(name, value);
|
||
|
if (!rc) {
|
||
|
DWORD error = GetLastError();
|
||
|
__kmp_fatal(KMP_MSG(CantSetEnvVar, name), KMP_ERR(error), __kmp_msg_null);
|
||
|
}
|
||
|
#else
|
||
|
#error Unknown or unsupported OS.
|
||
|
#endif
|
||
|
|
||
|
} // func __kmp_env_set
|
||
|
|
||
|
void __kmp_env_unset(char const *name) {
|
||
|
|
||
|
#if KMP_OS_UNIX
|
||
|
unsetenv(name);
|
||
|
#elif KMP_OS_WINDOWS
|
||
|
BOOL rc = SetEnvironmentVariable(name, NULL);
|
||
|
if (!rc) {
|
||
|
DWORD error = GetLastError();
|
||
|
__kmp_fatal(KMP_MSG(CantSetEnvVar, name), KMP_ERR(error), __kmp_msg_null);
|
||
|
}
|
||
|
#else
|
||
|
#error Unknown or unsupported OS.
|
||
|
#endif
|
||
|
|
||
|
} // func __kmp_env_unset
|
||
|
|
||
|
/* Intel OpenMP RTL string representation of environment: just a string of
|
||
|
characters, variables are separated with vertical bars, e. g.:
|
||
|
|
||
|
"KMP_WARNINGS=0|KMP_AFFINITY=compact|"
|
||
|
|
||
|
Empty variables are allowed and ignored:
|
||
|
|
||
|
"||KMP_WARNINGS=1||"
|
||
|
*/
|
||
|
|
||
|
static void
|
||
|
___kmp_env_blk_parse_string(kmp_env_blk_t *block, // M: Env block to fill.
|
||
|
char const *env // I: String to parse.
|
||
|
) {
|
||
|
|
||
|
char const chr_delimiter = '|';
|
||
|
char const str_delimiter[] = {chr_delimiter, 0};
|
||
|
|
||
|
char *bulk = NULL;
|
||
|
kmp_env_var_t *vars = NULL;
|
||
|
int count = 0; // Number of used elements in vars array.
|
||
|
int delimiters = 0; // Number of delimiters in input string.
|
||
|
|
||
|
// Copy original string, we will modify the copy.
|
||
|
bulk = __kmp_str_format("%s", env);
|
||
|
|
||
|
// Loop thru all the vars in environment block. Count delimiters (maximum
|
||
|
// number of variables is number of delimiters plus one).
|
||
|
{
|
||
|
char const *ptr = bulk;
|
||
|
for (;;) {
|
||
|
ptr = strchr(ptr, chr_delimiter);
|
||
|
if (ptr == NULL) {
|
||
|
break;
|
||
|
}
|
||
|
++delimiters;
|
||
|
ptr += 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Allocate vars array.
|
||
|
vars = (kmp_env_var_t *)allocate((delimiters + 1) * sizeof(kmp_env_var_t));
|
||
|
|
||
|
// Loop thru all the variables.
|
||
|
{
|
||
|
char *var; // Pointer to variable (both name and value).
|
||
|
char *name; // Pointer to name of variable.
|
||
|
char *value; // Pointer to value.
|
||
|
char *buf; // Buffer for __kmp_str_token() function.
|
||
|
var = __kmp_str_token(bulk, str_delimiter, &buf); // Get the first var.
|
||
|
while (var != NULL) {
|
||
|
// Save found variable in vars array.
|
||
|
__kmp_str_split(var, '=', &name, &value);
|
||
|
KMP_DEBUG_ASSERT(count < delimiters + 1);
|
||
|
vars[count].name = name;
|
||
|
vars[count].value = value;
|
||
|
++count;
|
||
|
// Get the next var.
|
||
|
var = __kmp_str_token(NULL, str_delimiter, &buf);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Fill out result.
|
||
|
block->bulk = bulk;
|
||
|
block->vars = vars;
|
||
|
block->count = count;
|
||
|
}
|
||
|
|
||
|
/* Windows* OS (actually, DOS) environment block is a piece of memory with
|
||
|
environment variables. Each variable is terminated with zero byte, entire
|
||
|
block is terminated with one extra zero byte, so we have two zero bytes at
|
||
|
the end of environment block, e. g.:
|
||
|
|
||
|
"HOME=C:\\users\\lev\x00OS=Windows_NT\x00\x00"
|
||
|
|
||
|
It is not clear how empty environment is represented. "\x00\x00"?
|
||
|
*/
|
||
|
|
||
|
#if KMP_OS_WINDOWS
|
||
|
static void ___kmp_env_blk_parse_windows(
|
||
|
kmp_env_blk_t *block, // M: Env block to fill.
|
||
|
char const *env // I: Pointer to Windows* OS (DOS) environment block.
|
||
|
) {
|
||
|
|
||
|
char *bulk = NULL;
|
||
|
kmp_env_var_t *vars = NULL;
|
||
|
int count = 0; // Number of used elements in vars array.
|
||
|
int size = 0; // Size of bulk.
|
||
|
|
||
|
char *name; // Pointer to name of variable.
|
||
|
char *value; // Pointer to value.
|
||
|
|
||
|
if (env != NULL) {
|
||
|
|
||
|
// Loop thru all the vars in environment block. Count variables, find size
|
||
|
// of block.
|
||
|
{
|
||
|
char const *var; // Pointer to beginning of var.
|
||
|
int len; // Length of variable.
|
||
|
count = 0;
|
||
|
var =
|
||
|
env; // The first variable starts and beginning of environment block.
|
||
|
len = KMP_STRLEN(var);
|
||
|
while (len != 0) {
|
||
|
++count;
|
||
|
size = size + len + 1;
|
||
|
var = var + len +
|
||
|
1; // Move pointer to the beginning of the next variable.
|
||
|
len = KMP_STRLEN(var);
|
||
|
}
|
||
|
size =
|
||
|
size + 1; // Total size of env block, including terminating zero byte.
|
||
|
}
|
||
|
|
||
|
// Copy original block to bulk, we will modify bulk, not original block.
|
||
|
bulk = (char *)allocate(size);
|
||
|
KMP_MEMCPY_S(bulk, size, env, size);
|
||
|
// Allocate vars array.
|
||
|
vars = (kmp_env_var_t *)allocate(count * sizeof(kmp_env_var_t));
|
||
|
|
||
|
// Loop thru all the vars, now in bulk.
|
||
|
{
|
||
|
char *var; // Pointer to beginning of var.
|
||
|
int len; // Length of variable.
|
||
|
count = 0;
|
||
|
var = bulk;
|
||
|
len = KMP_STRLEN(var);
|
||
|
while (len != 0) {
|
||
|
// Save variable in vars array.
|
||
|
__kmp_str_split(var, '=', &name, &value);
|
||
|
vars[count].name = name;
|
||
|
vars[count].value = value;
|
||
|
++count;
|
||
|
// Get the next var.
|
||
|
var = var + len + 1;
|
||
|
len = KMP_STRLEN(var);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Fill out result.
|
||
|
block->bulk = bulk;
|
||
|
block->vars = vars;
|
||
|
block->count = count;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* Unix environment block is a array of pointers to variables, last pointer in
|
||
|
array is NULL:
|
||
|
|
||
|
{ "HOME=/home/lev", "TERM=xterm", NULL }
|
||
|
*/
|
||
|
|
||
|
#if KMP_OS_UNIX
|
||
|
static void
|
||
|
___kmp_env_blk_parse_unix(kmp_env_blk_t *block, // M: Env block to fill.
|
||
|
char **env // I: Unix environment to parse.
|
||
|
) {
|
||
|
char *bulk = NULL;
|
||
|
kmp_env_var_t *vars = NULL;
|
||
|
int count = 0;
|
||
|
size_t size = 0; // Size of bulk.
|
||
|
|
||
|
// Count number of variables and length of required bulk.
|
||
|
{
|
||
|
while (env[count] != NULL) {
|
||
|
size += KMP_STRLEN(env[count]) + 1;
|
||
|
++count;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Allocate memory.
|
||
|
bulk = (char *)allocate(size);
|
||
|
vars = (kmp_env_var_t *)allocate(count * sizeof(kmp_env_var_t));
|
||
|
|
||
|
// Loop thru all the vars.
|
||
|
{
|
||
|
char *var; // Pointer to beginning of var.
|
||
|
char *name; // Pointer to name of variable.
|
||
|
char *value; // Pointer to value.
|
||
|
size_t len; // Length of variable.
|
||
|
int i;
|
||
|
var = bulk;
|
||
|
for (i = 0; i < count; ++i) {
|
||
|
KMP_ASSERT(var < bulk + size);
|
||
|
[[maybe_unused]] size_t ssize = size - (var - bulk);
|
||
|
// Copy variable to bulk.
|
||
|
len = KMP_STRLEN(env[i]);
|
||
|
KMP_MEMCPY_S(var, ssize, env[i], len + 1);
|
||
|
// Save found variable in vars array.
|
||
|
__kmp_str_split(var, '=', &name, &value);
|
||
|
vars[i].name = name;
|
||
|
vars[i].value = value;
|
||
|
// Move pointer.
|
||
|
var += len + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Fill out result.
|
||
|
block->bulk = bulk;
|
||
|
block->vars = vars;
|
||
|
block->count = count;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void __kmp_env_blk_init(kmp_env_blk_t *block, // M: Block to initialize.
|
||
|
char const *bulk // I: Initialization string, or NULL.
|
||
|
) {
|
||
|
|
||
|
if (bulk != NULL) {
|
||
|
___kmp_env_blk_parse_string(block, bulk);
|
||
|
} else {
|
||
|
#if KMP_OS_UNIX
|
||
|
___kmp_env_blk_parse_unix(block, environ);
|
||
|
#elif KMP_OS_WINDOWS
|
||
|
{
|
||
|
char *mem = GetEnvironmentStrings();
|
||
|
if (mem == NULL) {
|
||
|
DWORD error = GetLastError();
|
||
|
__kmp_fatal(KMP_MSG(CantGetEnvironment), KMP_ERR(error),
|
||
|
__kmp_msg_null);
|
||
|
}
|
||
|
___kmp_env_blk_parse_windows(block, mem);
|
||
|
FreeEnvironmentStrings(mem);
|
||
|
}
|
||
|
#else
|
||
|
#error Unknown or unsupported OS.
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
} // __kmp_env_blk_init
|
||
|
|
||
|
static int ___kmp_env_var_cmp( // Comparison function for qsort().
|
||
|
kmp_env_var_t const *lhs, kmp_env_var_t const *rhs) {
|
||
|
return strcmp(lhs->name, rhs->name);
|
||
|
}
|
||
|
|
||
|
void __kmp_env_blk_sort(
|
||
|
kmp_env_blk_t *block // M: Block of environment variables to sort.
|
||
|
) {
|
||
|
|
||
|
qsort(CCAST(kmp_env_var_t *, block->vars), block->count,
|
||
|
sizeof(kmp_env_var_t),
|
||
|
(int (*)(void const *, void const *)) & ___kmp_env_var_cmp);
|
||
|
|
||
|
} // __kmp_env_block_sort
|
||
|
|
||
|
void __kmp_env_blk_free(
|
||
|
kmp_env_blk_t *block // M: Block of environment variables to free.
|
||
|
) {
|
||
|
|
||
|
KMP_INTERNAL_FREE(CCAST(kmp_env_var_t *, block->vars));
|
||
|
__kmp_str_free(&(block->bulk));
|
||
|
|
||
|
block->count = 0;
|
||
|
block->vars = NULL;
|
||
|
|
||
|
} // __kmp_env_blk_free
|
||
|
|
||
|
char const * // R: Value of variable or NULL if variable does not exist.
|
||
|
__kmp_env_blk_var(kmp_env_blk_t *block, // I: Block of environment variables.
|
||
|
char const *name // I: Name of variable to find.
|
||
|
) {
|
||
|
|
||
|
int i;
|
||
|
for (i = 0; i < block->count; ++i) {
|
||
|
if (strcmp(block->vars[i].name, name) == 0) {
|
||
|
return block->vars[i].value;
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
|
||
|
} // __kmp_env_block_var
|
||
|
|
||
|
// end of file //
|