diff --git a/libmaxsi/Makefile b/libmaxsi/Makefile
index 72f8f3a2..528611cf 100644
--- a/libmaxsi/Makefile
+++ b/libmaxsi/Makefile
@@ -50,6 +50,7 @@ start.o \
time.o \
random.o \
abs.o \
+env.o \
integer.o \
c++.o \
memory.o \
diff --git a/libmaxsi/env.cpp b/libmaxsi/env.cpp
new file mode 100644
index 00000000..64444b9d
--- /dev/null
+++ b/libmaxsi/env.cpp
@@ -0,0 +1,192 @@
+/*******************************************************************************
+
+ COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2012.
+
+ This file is part of LibMaxsi.
+
+ LibMaxsi is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ LibMaxsi 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 Lesser General Public License for more
+ details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with LibMaxsi. If not, see .
+
+ env.cpp
+ Environmental variables utilities.
+
+*******************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+
+/* Since legacy applications rely on being able to modify the environ variable,
+ we have to keep track of whether it is what we expect and whether we know the
+ size of it. If it changed, we need to recount, as well as malloc a copy of it
+ if we wish to make changes. */
+extern "C" { char** environ = NULL; }
+static char** environ_malloced = NULL;
+static char** environ_counted = NULL;
+size_t environlen = 0;
+size_t environroom = 0;
+
+static inline bool environismalloced()
+{
+ return environ && environ == environ_malloced;
+}
+
+extern "C" const char* const* getenviron(void)
+{
+ return environ;
+}
+
+extern "C" size_t envlength(void)
+{
+ if ( !environ ) { return 0; }
+ if ( environ_counted != environ )
+ {
+ size_t count = 0;
+ while ( environ[count] ) { count++; }
+ environ_counted = environ;
+ environlen = count;
+ }
+ return environlen;
+}
+
+extern "C" const char* getenvindexed(size_t index)
+{
+ if ( envlength() <= index ) { errno = EBOUND; return NULL; }
+ return environ[index];
+}
+
+extern "C" const char* sortix_getenv(const char* name)
+{
+ size_t equalpos = strcspn(name, "=");
+ if ( name[equalpos] == '=' ) { return NULL; }
+ size_t namelen = equalpos;
+ size_t envlen = envlength();
+ for ( size_t i = 0; i < envlen; i++ )
+ {
+ if ( strncmp(name, environ[i], namelen) ) { continue; }
+ if ( environ[i][namelen] != '=' ) { continue; }
+ return environ[i] + namelen + 1;
+ }
+ return NULL;
+}
+
+extern "C" char* getenv(const char* name)
+{
+ return (char*) sortix_getenv(name);
+}
+
+static bool makeenvironmalloced()
+{
+ if ( environismalloced() ) { return true; }
+ size_t envlen = envlength();
+ size_t newenvlen = envlen;
+ size_t newenvsize = sizeof(char*) * (newenvlen+1);
+ char** newenviron = (char**) malloc(newenvsize);
+ if ( !newenviron ) { return false; }
+ size_t sofar = 0;
+ for ( size_t i = 0; i < envlen; i++ )
+ {
+ newenviron[i] = strdup(environ[i]);
+ if ( !newenviron[i] ) { goto cleanup; }
+ sofar = i;
+ }
+ newenviron[envlen] = NULL;
+ environlen = environroom = newenvlen;
+ environ = environ_malloced = environ_counted = newenviron;
+ return true;
+cleanup:
+ for ( size_t i = 0; i < sofar; i++ ) { free(newenviron[i]); }
+ free(newenviron);
+ return false;
+}
+
+extern "C" int clearenv(void)
+{
+ if ( !environ ) { return 0; }
+ if ( environismalloced() )
+ {
+ for ( char** varp = environ; *varp; varp++ ) { free(*varp); }
+ free(environ);
+ }
+ environ = environ_counted = environ_malloced = NULL;
+ return 0;
+}
+
+static bool doputenv(char* str, char* freeme, bool overwrite)
+{
+ if ( !makeenvironmalloced() ) { free(freeme); return false; }
+ size_t strvarnamelen = strcspn(str, "=");
+ for ( size_t i = 0; i < envlength(); i++ )
+ {
+ char* var = environ[i];
+ if ( strncmp(str, var, strvarnamelen) ) { continue; }
+ if ( !overwrite ) { free(freeme); return true; }
+ free(var);
+ environ[i] = str;
+ return true;
+ }
+ if ( environlen == environroom )
+ {
+ size_t newenvironroom = environroom ? 2 * environroom : 16;
+ size_t newenvironsize = sizeof(char*) * (newenvironroom+1);
+ char** newenviron = (char**) realloc(environ, newenvironsize);
+ if ( !newenviron ) { free(freeme); return false; }
+ environ = environ_malloced = environ_counted = newenviron;
+ environroom = newenvironroom;
+ }
+ environ[environlen++] = str;
+ environ[environlen] = NULL;
+ return true;
+}
+
+extern "C" int setenv(const char* name, const char* value, int overwrite)
+{
+ if ( !name || !name[0] || strchr(name, '=') ) { errno = EINVAL; return -1; }
+ char* str = (char*) malloc(strlen(name) + 1 + strlen(value) + 1);
+ if ( !str ) { return -1; }
+ stpcpy(stpcpy(stpcpy(str, name), "="), value);
+ if ( !doputenv(str, str, overwrite) ) { return -1; }
+ return 0;
+}
+
+extern "C" int putenv(char* str)
+{
+ if ( !strchr(str, '=') ) { errno = EINVAL; return -1; }
+ if ( !doputenv(str, NULL, true) ) { return -1; }
+ return 0;
+}
+
+extern "C" int unsetenv(const char* name)
+{
+ size_t equalpos = strcspn(name, "=");
+ if ( name[equalpos] == '=' ) { return 0; }
+ size_t namelen = equalpos;
+ size_t envlen = envlength();
+ for ( size_t i = 0; i < envlen; i++ )
+ {
+ if ( strncmp(name, environ[i], namelen) ) { continue; }
+ if ( environ[i][namelen] != '=' ) { continue; }
+ if ( environismalloced() )
+ {
+ char* str = environ[i];
+ free(str);
+ }
+ if ( i < envlen-1 ) { environ[i] = environ[envlen-1]; }
+ environ[--environlen] = NULL;
+ return 0;
+ }
+ return 0;
+}
+
diff --git a/libmaxsi/include/stdlib.h b/libmaxsi/include/stdlib.h
index f7faea4a..92cfb470 100644
--- a/libmaxsi/include/stdlib.h
+++ b/libmaxsi/include/stdlib.h
@@ -56,20 +56,39 @@ void* calloc(size_t, size_t);
void exit(int);
void _Exit(int status);
void free(void*);
-char* getenv(const char*);
long labs(long);
long long llabs(long long);
void* malloc(size_t);
#if !defined(_SORTIX_SOURCE)
char* mktemp(char* templ);
#endif
+int putenv(char*);
void qsort(void*, size_t, size_t, int (*)(const void*, const void*));
int rand(void);
void* realloc(void*, size_t);
+int setenv(const char*, const char*, int);
long strtol(const char* restrict, char** restrict, int);
unsigned long strtoul(const char* restrict, char** restrict, int);
unsigned long long strtoull(const char* restrict, char** restrict, int);
long long strtoll(const char* restrict, char** restrict, int);
+int unsetenv(const char*);
+
+#if defined(_SORTIX_SOURCE) || defined(_WANT_SORTIX_ENV)
+const char* const* getenviron(void);
+size_t envlength(void);
+const char* getenvindexed(size_t index);
+const char* sortix_getenv(const char* name);
+#endif
+#if (defined(_SOURCE_SOURCE) && __SORTIX_STDLIB_REDIRECTS) || \
+ defined(_WANT_SORTIX_ENV)
+const char* getenv(const char* name) asm ("sortix_getenv");
+#else
+char* getenv(const char*);
+#endif
+#if defined(_SORTIX_SOURCE) || defined(_SVID_SOURCE) || defined(_XOPEN_SOURCE) \
+ || defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
+int clearenv(void);
+#endif
/* TODO: These are not implemented in libmaxsi/sortix yet. */
#if defined(__SORTIX_SHOW_UNIMPLEMENTED)
@@ -99,11 +118,9 @@ long nrand48(unsigned short[3]);
int posix_memalign(void**, size_t, size_t);
int posix_openpt(int);
char* ptsname(int);
-int putenv(char*);
long random(void);
char* realpath(const char* restrict, char* restrict);
unsigned short *seed48(unsigned short [3]);
-int setenv(const char*, const char*, int);
void setkey(const char*);
char* setstate(char*);
void srand(unsigned);
@@ -114,7 +131,6 @@ float strtof(const char* restrict, char** restrict);
long double strtold(const char* restrict, char** restrict);
int system(const char*);
int unlockpt(int);
-int unsetenv(const char*);
size_t wcstombs(char* restrict, const wchar_t *restrict, size_t);
int wctomb(char*, wchar_t);
diff --git a/libmaxsi/process.cpp b/libmaxsi/process.cpp
index ab624d41..868ce177 100644
--- a/libmaxsi/process.cpp
+++ b/libmaxsi/process.cpp
@@ -40,8 +40,6 @@ namespace Maxsi
DEFN_SYSCALL0(pid_t, SysGetParentPID, SYSCALL_GETPPID);
DEFN_SYSCALL3(pid_t, SysWait, SYSCALL_WAIT, pid_t, int*, int);
- extern "C" char** environ = NULL;
-
void Abort()
{
// TODO: Send SIGABRT instead!
@@ -102,11 +100,6 @@ namespace Maxsi
{
return waitpid(-1, status, 0);
}
-
- extern "C" char* getenv(const char* name)
- {
- return NULL;
- }
}
}