blob: d9750ce84047ced0c253b4b35eb9b3eac06b9a1c [file] [log] [blame] [raw]
/* A part of the Windows CE C Extra Library (celibc)
Copyright 2007-2015 PC GO Ld.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*/
// Based on the GNU C Library
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <windows.h>
#include <wchar.h>
#define LOCK
#define UNLOCK
#ifdef USE_TSEARCH
#include <search.h>
/* This is a pointer to the root of the search tree with the known
values. */
static void *known_values;
# define KNOWN_VALUE(Str) \
({ \
void *value = tfind(Str, &known_values, (__compar_fn_t)strcmp); \
value != NULL ? *(char **) value : NULL; \
})
# define STORE_VALUE(Str) \
tsearch(Str, &known_values, (__compar_fn_t)strcmp)
#else
# define KNOWN_VALUE(Str) NULL
# define STORE_VALUE(Str) do { } while (0)
#endif
char **environ = NULL;
/* If this variable is not a null pointer we allocated the current environment. */
static char **last_environ;
/* This function is used by `setenv' and `putenv'. The difference between
the two functions is that for the former must create a new string which
is then placed in the environment, while the argument of `putenv'
must be used directly. This is all complicated by the fact that we try
to reuse values once generated for a `setenv' call since we can never
free the strings. */
static int __add_to_environ(const char *name, const char *value, const char *combined, int replace) {
register char **ep;
register size_t size;
const size_t namelen = strlen (name);
const size_t valuelen = value ? strlen (value) + 1 : 0;
LOCK;
/* We have to get the pointer now that we have the lock and not earlier
since another thread might have created a new environment. */
ep = environ;
size = 0;
if(ep) while(*ep) {
if(strncmp(*ep, name, namelen) == 0 && (*ep)[namelen] == '=') break;
size++;
ep++;
}
if(!ep || __builtin_expect(*ep == NULL, 1)) {
const size_t varlen = namelen + 1 + valuelen;
char **new_environ;
/* We allocated this space; we can extend it. */
new_environ = (char **)realloc(last_environ, (size + 2) * sizeof (char *));
if(!new_environ) {
UNLOCK;
return -1;
}
if(combined) new_environ[size] = (char *)combined;
else {
/* See whether the value is already known. */
#ifdef USE_TSEARCH
char *new_value = malloc(varlen);
if(!new_value) {
UNLOCK;
if(!last_environ) free(new_environ);
return -1;
}
//mempcpy(mempcpy(mempcpy(new_value, name, namelen), "=", 1), value, valuelen);
mempcpy(new_value, name, namelen);
new_value[namelen] = '=';
memcpy(new_value + namelen + 1, value, valuelen);
new_environ[size] = KNOWN_VALUE(new_value);
if(__builtin_expect(!new_environ[size], 1)) {
#endif
new_environ[size] = malloc(varlen);
if(!new_environ[size]) {
UNLOCK;
return -1;
}
#ifdef USE_TSEARCH
memcpy(new_environ[size], new_value, varlen);
#else
memcpy(new_environ[size], name, namelen);
new_environ[size][namelen] = '=';
memcpy(&new_environ[size][namelen + 1], value, valuelen);
#endif
STORE_VALUE(new_environ[size]);
#ifdef USE_TSEARCH
}
#endif
}
if(environ != last_environ) memcpy(new_environ, environ, size * sizeof(char *));
new_environ[size + 1] = NULL;
last_environ = environ = new_environ;
} else if(replace) {
char *np;
/* Use the user string if given. */
if(combined) np = (char *)combined;
else {
const size_t varlen = namelen + 1 + valuelen;
#ifdef USE_TSEARCH
char *new_value = malloc(varlen);
if(!new_value) {
UNLOCK;
return -1;
}
memcpy(new_value, name, namelen);
new_value[namelen] = '=';
memcpy(new_value + namelen + 1, value, valuelen);
np = KNOWN_VALUE(new_value);
if(__builtin_expect(!np, 1)) {
#endif
np = malloc(varlen);
if(!np) {
UNLOCK;
return -1;
}
#ifdef USE_TSEARCH
memcpy(np, new_value, varlen);
#else
memcpy(np, name, namelen);
np[namelen] = '=';
memcpy(np + namelen + 1, value, valuelen);
#endif
STORE_VALUE(np);
#ifdef USE_TSEARCH
}
#endif
}
*ep = np;
}
UNLOCK;
return 0;
}
int setenv(const char *name, const char *value, int replace) {
if(!name || !*name || strchr(name, '=')) {
errno = EINVAL;
return -1;
}
return __add_to_environ(name, value, NULL, replace);
}
int unsetenv(const char *name) {
if(!name || !*name || strchr(name, '=')) {
errno = EINVAL;
return -1;
}
size_t len = strlen(name);
LOCK;
char **ep = environ;
if(ep) while(*ep) {
if(strncmp(*ep, name, len) == 0 && (*ep)[len] == '=') {
char **dp = ep;
while(*dp) {
*dp = dp[1];
dp++;
}
} else ep++;
}
UNLOCK;
return 0;
}
int clearenv() {
LOCK;
if(environ) {
if(environ == last_environ) {
free(environ);
last_environ = NULL;
}
environ = NULL;
}
UNLOCK;
return 0;
}
char *getenv(const char *name) {
char **ep;
size_t len = strlen(name);
uint16_t name_start;
if(!name || !environ) return NULL;
if(!name[1]) {
/* The name of the variable consists of only one character. Therefore
the first two characters of the environment entry are this character
and a '=' character. */
#if __BYTE_ORDER == __LITTLE_ENDIAN || !_STRING_ARCH_unaligned
name_start = ('=' << 8) | *(const unsigned char *)name;
#else
# if __BYTE_ORDER == __BIG_ENDIAN
name_start = '=' | ((*(const unsigned char *)name) << 8);
# else
#error "Funny byte order."
# endif
#endif
for(ep = environ; *ep != NULL; ep++) {
#if _STRING_ARCH_unaligned
uint16_t ep_start = *(uint16_t *)*ep;
#else
uint16_t ep_start = (((unsigned char *)*ep)[0] | (((unsigned char *)*ep)[1] << 8));
#endif
if(name_start == ep_start) return *ep + 2;
}
} else {
#if _STRING_ARCH_unaligned
name_start = *(const uint16_t *)name;
#else
name_start = (((const unsigned char *)name)[0] | (((const unsigned char *)name)[1] << 8));
#endif
len -= 2;
name += 2;
for(ep = environ; *ep != NULL; ep++) {
#if _STRING_ARCH_unaligned
uint16_t ep_start = *(uint16_t *) *ep;
#else
uint16_t ep_start = (((unsigned char *) *ep)[0] | (((unsigned char *) *ep)[1] << 8));
#endif
if(name_start == ep_start && strncmp(*ep + 2, name, len) == 0 && (*ep)[len+2] == '=') {
return *ep + len + 3;
}
}
}
return NULL;
}
int putenv(char *s) {
const char *name_end = strchr(s, '=');
if(!name_end) {
unsetenv(s);
return 0;
}
size_t len = name_end - s;
char name[len + 1];
memcpy(name, s, len);
name[len] = 0;
return __add_to_environ(name, NULL, s, 1);
}
static int __putenv_c(const char *s) {
const char *name_end = strchr(s, '=');
if(!name_end) return -1;
size_t len = name_end - s;
char name[len + 1];
memcpy(name, s, len);
name[len] = 0;
return __add_to_environ(name, name_end + 1, NULL, 1);
}
void __initenv_from_reg() {
int err;
//char valname[126];
//char data[1024];
wchar_t wvalname[256];
wchar_t wdata[1024];
char buf[1024];
HKEY hKey;
DWORD dwValSize;
DWORD dwDataSize;
DWORD dwType;
int idx = 0;
//char *p;
static int initted = 0;
if(initted) return;
initted = 1;
err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Environment", 0, KEY_READ, &hKey);
if(err) {
// use defaults
putenv("PATH=.:/:/Windows");
putenv("HOME=/");
putenv("TEMP=/Temp");
putenv("OS=Windows_CE");
return;
}
size_t len1;
size_t len2;
while(1) {
dwValSize = 1024;
dwDataSize = 4096;
//res = XCERegEnumValueA(hKey, idx++, valname, &dwValSize, NULL, &dwType, data, &dwDataSize);
err = RegEnumValueW(hKey, idx++, wvalname, &dwValSize, NULL, &dwType, (unsigned char *)wdata, &dwDataSize);
if(err) {
//if(res != ERROR_NO_MORE_ITEMS) XCEShowMessageA("RegEnumValue: %d", res);
break;
}
len1 = wcslen(wvalname) * 2;
len2 = wcslen(wdata) * 2;
//char *valname = (char *)malloc(len1 * sizeof(char));
//char *data = (char *)malloc(len2 * sizeof(char));
char valname[len1];
char data[len2];
wcstombs(valname, wvalname, len1);
wcstombs(data, wdata, len2);
if(dwType != REG_SZ) continue;
sprintf(buf, "%s=%s", valname, data);
__putenv_c(buf);
}
RegCloseKey(hKey);
}
void __initenv_from_envblock(const char *s) {
while(*s) {
__putenv_c(s);
s += strlen(s) + 1;
}
}
void __initenv(const char *s) {
if(s) __initenv_from_envblock(s);
else __initenv_from_reg();
}
void _initenv(const char *s) {
__initenv(s);
}