expansion.c

DownloadView Raw

/**
 * expansion.c
 *
 * Demonstrates one approach for implementing environment variable expansion in
 * strings.
 */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

/**
 * Expands environment variables (identified by $ prefix, e.g., $SHELL) in a
 * string by resizing and inserting the value of the variable into the string.
 * This function only does one expansion, but can be called multiple times on a
 * string to expand more than one variable.
 * 
 * NOTE: this function allocates memory! The caller is responsible for freeing
 * the memory. 
 *
 * Parameters:
 * - str: The string with variables to expand
 *
 * Returns: char pointer to the newly-expanded and allocated string. Returns
 * NULL if there are no variables to replace or if memory cannot be allocated.
 */
char *expand_var(char *str)
{
    size_t var_start = 0;
    var_start = strcspn(str, "$");
    if (var_start == strlen(str)) {
        /* No variable to replace */
        return NULL;
    }

    size_t var_len = strcspn(str + var_start, " \t\r\n\'\"");

    char *var_name = malloc(sizeof(char) * var_len + 1);
    if (var_name == NULL) {
        return NULL;
    }
    strncpy(var_name, str + var_start, var_len);
    var_name[var_len] = '\0';

    if (strlen(var_name) <= 1) {
        free(var_name);
        return NULL;
    }

    /* Use index 1 to ignore the '$' prefix */
    char *value = getenv(&var_name[1]);
    if (value == NULL) {
        fprintf(stderr, "value was null\n");
        value = "";
    }

    fprintf(stderr, "Replacing variable: %s='%s'\n", var_name, value);
    free(var_name);

    /* Grab the size of the remaining string (after the $var): */
    size_t remain_sz = strlen(str + var_start + var_len);

    /* Our final string contains the prefix data, the value of the variable, the
     * remaining string size, and an extra character for the NUL byte. */
    size_t newstr_sz = var_start + strlen(value) + remain_sz + 1;

    char *newstr = malloc(sizeof(char) * newstr_sz);
    if (newstr == NULL) {
        return NULL;
    }

    strncpy(newstr, str, var_start);
    newstr[var_start] = '\0';
    strcat(newstr, value);
    strcat(newstr, str + var_start + var_len);

    return newstr;
}