sortix-mirror/libmaxsi/env.cpp

202 lines
5.7 KiB
C++

/*******************************************************************************
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 <http://www.gnu.org/licenses/>.
env.cpp
Environmental variables utilities.
*******************************************************************************/
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
/* 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; }
// TODO: HACK: This voilates POSIX as it mandates that callers must be able
// to modify the environment by modifying the string. However, this is bad
// design! It also means we got a memory leak since we can't safely free
// strings from the environment when overriding them. Therefore we create
// a copy of the strings here and use the copy instead. This is a problem
// for some applications that will subtly break.
char* strcopy = strdup(str);
if ( !strcopy )
return -1;
if ( !doputenv(strcopy, strcopy, 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;
}