libenv is a library for modifying environment variables under UNIX like operating systems. Included is a wrapper library called posix_env that implements a POSIX compliant interface.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
libenv-1.0.0/libenv.c

890 lines
18 KiB

/*#############################################################################
# #
# Copyright 2022 TruHobbyist #
# #
# Redistribution and use in source and binary forms, with or #
# without modification, are permitted provided that the following #
# conditions are met: #
# #
# 1. Redistributions of source code must retain the above copyright #
# notice, this list of conditions and the following disclaimer. #
# #
# 2. Redistributions in binary form must reproduce the above #
# copyright notice, this list of conditions and the following #
# disclaimer in the documentation and/or other materials provided #
# with the distribution. #
# #
# 3. Neither the name of the copyright holder nor the names of its #
# contributors may be used to endorse or promote products derived #
# from this software without specific prior written permission. #
# #
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS #
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT #
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS #
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE #
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, #
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES #
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR #
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) #
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, #
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) #
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF #
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #
# #
#############################################################################*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libenv.h"
// NOTE: Private functions
int __env_prefix_in_var(
const char *name, // - IN: Variable name to look for
char *var_pointer, // - IN: Environment variable name
unsigned int *prefix_was_found, // - OUT: Flag indicating that name + '=' is a prefix of var_pointer
char **value // - OUT: Pointer to value of environment variable (if prefix was found)
)
{
unsigned int name_length;
unsigned int var_index;
unsigned int var_pointer_length;
unsigned int name_is_equal;
// NOTE: Argument checking
//
// This private function assumes the arguments are already validated
// by the caller.
//
// Specifically:
//
// - name is non-NULL and not empty
// - var_pointer is non-NULL and not empty
// - name_is_equal is a writable memory location of 4 bytes in size
// Initialize out values
*prefix_was_found = 0;
*value = NULL;
// Get length of variable name
name_length = 1;
while (name[name_length] != NULL)
{
name_length++;
}
// Get length of environment variable name
var_pointer_length = 1;
while (var_pointer[var_pointer_length] != NULL)
{
var_pointer_length++;
}
// Check for correct length
if (var_pointer_length <= name_length)
{
// NOTE: Variable name is too long
return 0; // ERROR: Variable name is too long
}
// NOTE: Prefix
//
// The prefix consists of the environment variable name plus
// the '=' character.
// Traverse environment variable
name_is_equal = 1;
for (var_index = 0; var_index < name_length; var_index++)
{
// Compare character by character
if (var_pointer[var_index] != name[var_index])
{
name_is_equal = 0;
break;
}
}
// Check for equal name
if (name_is_equal != 1)
{
// NOTE: Name not equal
//
// var_pointer does not begin with name.
return -1; // ERROR: Name not equal
}
// HINT: var_pointer begins with name
// Check for '=' after variable name
if (var_pointer[name_length] != '=')
{
// NOTE: No '=' after variable name
//
// This happens when name is a substring of
// another, longer, environment variable name.
//
// For example:
//
// name = "MYVAR";
// var_pointer = "MYVAR_JUST_LONGER=MYVALUE"
return -2; // ERROR: No '=' after variable name
}
// Set out values
*prefix_was_found = 1;
*value = &(var_pointer[name_length + 1]);
return 1; // SUCCESS
}
int __env_traverse_vars(
char **vars, // - IN: Environment variables
const char *name, // - IN: Variable name to look for
unsigned int *prefix_was_found, // - OUT: Flag indicating that name + '=' is a prefix of var_pointer
char **value, // - OUT: Pointer to value of environment variable (if prefix was found)
unsigned int *var_index // - OUT: Index of pointer to environment variable given by name
)
{
char *var_pointer;
// NOTE: Argument checking
//
// This private function assumes the arguments are already validated
// by the caller.
//
// Specifically:
//
// - vars points to an array of pointers to strings of the form "NAME=VALUE"
// - name is non-NULL and not empty
// - prefix_was_found is a writable memory location of 4 bytes in size
// - value is a writable memory location of 4 bytes in size
// Initialize out values
*prefix_was_found = 0;
*value = NULL;
*var_index = 0;
// Traverse environment variables
*prefix_was_found = 0;
*value = NULL;
while (vars[*var_index] != NULL)
{
// Get variable pointer
var_pointer = vars[*var_index];
// Check for prefix in environment variable
if (__env_prefix_in_var(
name,
var_pointer,
prefix_was_found,
value
) < 1)
{
// NOTE: Prefix not found
// Increment variable pointer index
(*var_index)++;
continue;
}
// HINT: - prefix_was_found is set to 1 if var_pointer begins with: name + '='
// HINT: - *value points to the beginning of the variable value
// HINT: - Otherwise prefix_was_found is set to 0
// HINT: - value is set to NULL
// Check for found prefix
if (*prefix_was_found == 1)
{
// NOTE: Variable prefix found
break;
}
// Increment variable pointer index
(*var_index)++;
}
// Check for found prefix
if (*prefix_was_found != 1)
{
// Reset out value
*var_index = 0;
}
return 1; // SUCCESS
}
// NOTE: Public functions
int env_init(
char **envp, // - IN: Environment pointer
env_t *env // - OUT: Environment descriptor
)
{
unsigned int var_index;
unsigned int var_length;
unsigned int pointer_index;
unsigned int pointer_num;
// IN: Check for environment pointer
if (envp == NULL)
{
return 0; // ERROR: No environment pointer
}
// OUT: Check for environment descriptor
if (env == NULL)
{
return -1; // ERROR: No environment descriptor
}
// NOTE: Copy environment
//
// Create a new environment in dynamic memory.
// Get number of environment pointers
pointer_num = 0;
while (envp[pointer_num] != NULL)
{
pointer_num++;
}
// Count last NULL pointer
pointer_num++;
// Allocate environment pointers
env->vars = (char **) malloc(pointer_num * sizeof(char *));
if (env->vars == NULL)
{
return -2; // ERROR: Couldn't allocate environment pointers
}
// Traverse through pointers
for (pointer_index = 0; pointer_index < (pointer_num - 1); pointer_index++)
{
// Get length of environment variable at pointer index
var_length = strlen(envp[pointer_index]);
// Allocate new environment variable
env->vars[pointer_index] = (char *) malloc(
var_length + // HINT: Environment variable
1 // HINT: Terminating NULL byte
);
if (env->vars[pointer_index] == NULL)
{
return -3; // ERROR: Couldn't allocate environment variable
}
#if 0
// DEBUG
printf("[main] MALLOC: index=%i pointer=%p\n",
pointer_index,
env->vars[pointer_index]);
#endif
// Copy environment variable
for (var_index = 0; var_index < var_length; var_index++)
{
// Copy char by char
env->vars[pointer_index][var_index] = envp[pointer_index][var_index];
}
// Set terminating NULL byte
env->vars[pointer_index][var_length] = '\0';
// Increment size of pointer array
env->vars_size++;
}
// Set terminating NULL pointer
env->vars[pointer_num - 1] = NULL;
// Increment size of pointer array for NULL pointer
env->vars_size++;
return 1; // SUCCESS
}
int env_get(
env_t *env, // - IN: Environment descriptor
const char *name, // - IN: Environment variable name
char **value // - OUT: Environment variable value
)
{
unsigned int var_index;
unsigned int prefix_was_found;
// IN: Check for environment descriptor
if (env == NULL)
{
return 0; // ERROR: No environment descriptor
}
// IN: Check for environment variable name
if (name == NULL)
{
return -1; // ERROR: No environment variable name
}
// OUT: Check for environment variable value
if (value == NULL)
{
return -2; // ERROR: No environment variable value
}
// Check for empty name string
if (name[0] == '\0')
{
return -3; // ERROR: Empty environment variable name
}
// Traverse environment variables
prefix_was_found = 0;
*value = NULL;
__env_traverse_vars(
env->vars,
name,
&prefix_was_found,
value,
&var_index
);
// HINT: - If prefix_was_found is set to 1, *value is set to the value of the environment variable
// given by name, var_index is set to the index of name in env->vars
// HINT: - Otherwise, prefix_was_found is set to 0, *value is set to NULL, var_index is set to 0
// Check for found variable name
if (*value == NULL)
{
return -4; // ERROR: Variable name not found
}
return 1; // SUCCESS
}
int env_set(
env_t *env, // - IN: Environment descriptor
const char *name, // - IN: Environment variable name
const char *value // - IN: Environment variable value
)
{
unsigned int var_index;
unsigned int var_size;
unsigned int var_was_found;
unsigned int name_index;
unsigned int name_length;
unsigned int name_has_equal;
unsigned int value_index;
unsigned int value_length;
char *var_value;
char *var_pointer;
char **array_pointers;
// IN: Check for environment descriptor
if (env == NULL)
{
return 0; // ERROR: No environment descriptor
}
// IN: Check for environment variable name
if (name == NULL)
{
return -1; // ERROR: No environment variable name
}
// OUT: Check for environment variable value
if (value == NULL)
{
return -2; // ERROR: No environment variable value
}
// Check for empty environment variable name
if (name[0] == '\0')
{
return -3; // ERROR: Empty environment variable name
}
// NOTE: Empty value
//
// By contrast to above check, the value may be the empty string.
// Get length of variable name
name_has_equal = 0;
name_length = 0;
while (name[name_length] != NULL)
{
// Check for '=' character
if (name[name_length] == '=')
{
name_has_equal = 1;
break;
}
name_length++;
}
// Check for '=' character
if (name_has_equal == 1)
{
return -4; // ERROR: name contains a '=' character
}
// Traverse environment variables
var_was_found = 0;
var_value = NULL;
var_index = 0;
__env_traverse_vars(
env->vars,
name,
&var_was_found,
&var_value,
&var_index
);
// NOTE: Allocation order
//
// Storage for the new variable is allocated in advance,
// at least before freeing old storage or allocating new one.
//
// This is because POSIX requires to leave the environment in the
// same state if allocation for the new variable could not be
// done.
//
// If this variable allocation comes after freeing current storage or
// after extending the array of pointers, undoing changes will be
// more complicated.
// Get length of variable value
value_length = 1;
while (value[value_length] != NULL)
{
value_length++;
}
// Calculate size of new environment variable
var_size = name_length +
1 + // HINT: '='
value_length +
1; // HINT: NULL byte
// Allocate new environment variable
var_pointer = (char *) malloc(var_size);
if (var_pointer == NULL)
{
return -5; // ERROR: Couldn't allocate environment variable
}
// INFO: Storage for new variable is allocated, now handle array of pointers.
#if 0
printf("[env_set] MALLOC: index=%i pointer=%p\n",
var_index,
var_pointer);
#endif
if (var_was_found == 1)
{
// NOTE: Variable found
//
// Free allocated storage for current variable.
#if 0
printf("[env_set] FREE: index=%i pointer=%p\n",
var_index,
env->vars[var_index]);
#endif
// Free allocated storage
free(env->vars[var_index]);
// Set new variable pointer
env->vars[var_index] = var_pointer;
}
else
{
// NOTE: Variable not found
//
// Allocate new variable pointer.
// Save current array starting address
array_pointers = env->vars;
// Extend array of pointers
env->vars = (char **) realloc (
env->vars,
(
env->vars_size +
1 // HINT: + 1 for the new variable pointer
) * sizeof(char *)
);
if (env->vars == NULL)
{
// Restore array starting address
env->vars = array_pointers;
// Free storage for new variable
free(var_pointer);
return -6; // ERROR: Couldn't add new variable pointer
}
// INFO: New variable pointer added to array
// Update array size
env->vars_size++;
// Set new variable index
var_index = env->vars_size - 2;
// Set terminating NULL pointer
env->vars[env->vars_size - 1] = NULL;
// Set new variable pointer
env->vars[var_index] = var_pointer;
}
// INFO:
//
// var_index contains the index of the new variable pointer.
//
// If variable was found, its storage space is freed, so
// in either case, storage for the new variable needs to be
// allocated.
// NOTE: Copy new name and value
// Copy name
for (name_index = 0; name_index < name_length; name_index++)
{
var_pointer[name_index] = name[name_index];
}
// Append '='
var_pointer[name_length] = '=';
// Append value
for (value_index = 0; value_index < value_length; value_index++)
{
var_pointer
[
name_length +
1 + // HINT: One index after '='
value_index
] = value[value_index];
}
// Append '\0'
var_pointer[name_length + 1 + value_length] = '\0';
return 1; // SUCCESS
}
int env_unset(
env_t *env, // - IN: Environment descriptor
const char *name // - IN: Environment variable name
)
{
unsigned int var_index;
unsigned int var_was_found;
unsigned int shift_index;
char *var_value;
char *free_pointer;
char **array_pointers;
// IN: Check for environment descriptor
if (env == NULL)
{
return 0; // ERROR: No environment descriptor
}
// IN: Check for environment variable name
if (name == NULL)
{
return -1; // ERROR: No environment variable name
}
// Check for empty name string
if (name[0] == '\0')
{
return -2; // ERROR: Empty name string
}
// Traverse environment variables
var_was_found = 0;
var_value = NULL;
var_index = 0;
__env_traverse_vars(
env->vars,
name,
&var_was_found,
&var_value,
&var_index
);
if (var_was_found == 1)
{
// NOTE: Variable found
// Save pointer to variable
free_pointer = env->vars[var_index];
// Save current array starting address
array_pointers = env->vars;
// Reduce array of pointers
env->vars = (char **) realloc (
env->vars,
(env->vars_size - 1) * sizeof(char *)
);
if (env->vars == NULL)
{
// NOTE: Couldn't reduce array of pointers
// Restore array starting address
env->vars = array_pointers;
return -3; // ERROR: Couldn't reduce array of pointers
}
// Update size of array of pointers
env->vars_size--;
// INFO: Array of pointers was reduced by one pointer
#if 0
// DEBUG: Traverse shiftable pointers
for (shift_index = var_index; shift_index < env->vars_size; shift_index++)
{
printf("[env_unset] INFO: Before: index=%i pointer=%p\n",
shift_index,
env->vars[shift_index]);
}
#endif
// Traverse shiftable pointers
for (shift_index = var_index; shift_index < (env->vars_size - 1); shift_index++)
{
// Shift pointer to the left by one
env->vars[shift_index] = env->vars[shift_index + 1];
}
// Set terminating NULL pointer
env->vars[env->vars_size - 1] = NULL;
#if 0
// DEBUG: Traverse shiftable pointers
for (shift_index = var_index; shift_index < env->vars_size; shift_index++)
{
printf("[env_unset] INFO: After: index=%i pointer=%p\n",
shift_index,
env->vars[shift_index]);
}
#endif
// Free variable pointer
free(free_pointer);
#if 0
printf("[env_unset] FREE: index=%i pointer=%p\n",
var_index,
free_pointer);
#endif
}
else
{
// NOTE: Variable not found
return -4; // ERROR: Variable not found
}
return 1; // SUCCESS
}
int env_debug(
env_t *env // - IN: Environment descriptor
)
{
unsigned int var_index;
unsigned int var_length;
char *var_pointer;
// IN: Check for environment descriptor
if (env == NULL)
{
return 0; // ERROR: No environment descriptor
}
// Check for environment variables
if (env->vars == NULL)
{
return -1; // ERROR: No environment variables
}
// Traverse environment variables
var_index = 0;
while (env->vars[var_index] != NULL)
{
// Get variable pointer
var_pointer = env->vars[var_index];
// Get length of variable
var_length = 0;
while (var_pointer[var_length] != NULL)
{
var_length++;
}
printf("[env_debug] INFO: %i. addr=%p length=%u\n",
var_index,
var_pointer,
var_length);
printf("[env_debug] INFO: %i. var=\"%s\"\n",
var_index,
var_pointer);
// Increment variable pointer index
var_index++;
}
// Display size of array of pointers
printf("[env_debug] INFO: vars_size=%u\n",
env->vars_size);
return 1; // SUCCESS
}