| /* A part of the Native C Library for Windows NT |
| 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 |
| |
| #define _NO_ENVIRON_VAR_DEF |
| |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <wchar.h> |
| #include <assert.h> |
| |
| //#include <stdio.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; |
| |
| // I can't export the variable environ to the library, so define this function with a diffent name |
| char ***_environ() { |
| return &environ; |
| } |
| |
| /* 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_envblock(const char *s) { |
| while(*s) { |
| __putenv_c(s); |
| s += strlen(s) + 1; |
| } |
| } |
| |
| void __initenv_from_wenvblock(const wchar_t *s) { |
| while(*s) { |
| size_t len = wcslen(s) + 1; |
| size_t mlen = len * 2 - 1; |
| char buffer[mlen]; |
| if(wcstombs(buffer, s, mlen) > 0) __putenv_c(buffer); |
| s += len; |
| } |
| } |
| |
| wchar_t *__environ_to_wenvblock(wchar_t *buffer, size_t buffer_size) { |
| wchar_t *p = buffer; |
| char **e = environ; |
| while(*e) { |
| int len = mbstowcs(p, *e++, buffer_size / sizeof(wchar_t) - 1); |
| if(len < 0) continue; |
| assert(len * sizeof(wchar_t) < buffer_size); |
| p += len + 1; |
| buffer_size -= len * sizeof(wchar_t); |
| if(buffer_size / sizeof(wchar_t) - 1 == len || buffer_size < sizeof(wchar_t) * 3) { |
| p[-1] = 0; |
| break; |
| } |
| } |
| *p = 0; |
| return buffer; |
| } |