0

i am implementing setenv && unsetenv

My logic is to edit the .bashrc file, seeing as using IPC didn't work for me

My problem is with the _unsetenv function, when I try to remove an environment variable, it removes it but adds extra text to the last line

for example, I have these variable set(from line 122 - 126) and i want to delete 'FOO':

export R='make re'
export FOO=BAR
export PATH=/home/marlon/learning_programming/C/shell:$PATH
export CRAB=VALUE
export SOME='CHANGED'

now after executing _unsetenv my .bashrc file looks like this:

export R='make re'
export PATH=/home/marlon/learning_programming/C/shell:$PATH
export CRAB=VALUE
export SOME='CHANGED'
SOME='CHANGED'

why is it adding the line: SOME='CHANGED'?

here's some helper functions:

#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/mman.h>

/**
 * string_count - counts the words in a string separated by a delimiter
 * and the letters befor a delimiter.
 * @str: string to be counted.
 * @token: string of delimiters.
 *
 * Return: an array of:
 * a pointer to the number of words
 * an array of the letters before each delimiter.
 */
int **string_count(char *str, char *token)
{
    int words, *before_delim, letters, i, j, k;
    int **how_many;
    char flag;


    how_many = malloc(sizeof(int *) * 2);
    if (!how_many)
        return (NULL);

    words = 0;
    for (i = 0; str[i]; i++)
    {
        for (j = 0; token[j]; j++)
        {
            if (str[i] == token[j])
                words++;
        }
    }
    words++;

    /* count the letters before delimiters */

    before_delim = malloc(sizeof(int) * words);
    if (!before_delim)
        return (NULL);

    i = 0;
    for (k = 0; k < words; k++)
    {
        flag = 0;
        letters = 0;
        for (; str[i]; i++)
        {
            for (j = 0; token[j]; j++)
            {
                if (str[i] == token[j])
                {
                    flag = 1;
                    i++;
                }
            }
            if (flag)
            {
                break;
            }
            else
                letters++;
        }
        before_delim[k] = letters;
    }

    how_many[0] = &words;
    how_many[1] = before_delim;


    return (how_many);
}

/**
 * file_count - counts a file.
 * Description: COUNT[0]: number of lines in the file
 * COUNT[1]: line number where sub occurs, 0 if not found or empty.
 * COUNT[2]: number of letters on each line.
 * A string can be searched for in a file using sub.
 * Set sub to "" if no string should be searched for.
 * @stream: an opened file descriptor.
 * @sub: an optional string to be searched for in a file.
 * Can be set to "" for no search.
 *
 * Return: an array of pointers on the heap to:
 * the number of lines in the file
 * the line number where sub occurs
 * the number of letters on each line
 */
int **file_count(char *filename, char *sub)
{
    char *buffer = 0, flag = 0;
    size_t n = 0, l1;
    ssize_t nread;
    FILE *stream;
    int **count, i, j, k;
    int *letters;

    /* opening file */
    stream = fopen(filename, "r");
    if (!stream)
        return (NULL);

    count = malloc(sizeof(int *) * 3);
    if (!count)
        return (NULL);

    i = j = 0;
    count[0] = 0;
    l1 = strlen(sub);

    /* reading file line by line */
    while ((nread = getline(&buffer, &n, stream)) != -1)
    {
        /* if found string to be searched, stop incrementing j */
        if (!(strncmp(sub, buffer, l1)))
        {
            j++;
            flag = 1;
        }

        /* counting lines */
        i++;

        /* counting till string to be searched for is found */
        if (flag == 0)
            j++;
    }

    if (!l1 || !flag)
        j = 0;
    count[0] = &i;
    count[1] = &j;

    /* closing file for letter count */
    free(buffer);
    fclose(stream);

    /* counting letters on each line */

    /* opening file */
    stream = fopen(filename, "r");
    if (!stream)
        return (NULL);

    letters = malloc(sizeof(int) * i);
    if (!letters)
        return (NULL);

    buffer = 0;
    n = 0;
    i = 0;
    while ((nread = getline(&buffer, &n, stream)) != -1)
    {
        for (k = 0; buffer[k]; k++)
            ;
        letters[i] = k;
        i++;
    }

    count[2] = letters;

    free(buffer);
    fclose(stream);

    return (count);
}
/**
 * strsplt - splits a string by delimiter into an array of each word of the string.
 * @str: string to split.
 * @token: string of delimiters.
 * @new_delim: string to split new array by.
 *
 * Return: a new array of strings.
 */
char **strsplt(char *str, char *token, char new_delim)
{
    char **split;
    int *array;
    int index, watch, assign, letters;
    int **str_count, wc;


    str_count = string_count(str, token);
    wc = **str_count;

    split = malloc(sizeof(char *) * (wc + 1));
    if (!split)
        return (NULL);

    array = str_count[1];
    assign = 0;
    for (index = 0; index < wc; index++)
    {
        letters = array[index];
        split[index] = malloc(sizeof(char) * letters + 2);
        if (!split[index])
            return (NULL);

        for (watch = 0; watch < letters; watch++)
        {
            split[index][watch] = str[assign++];
        }
        assign++;
        split[index][watch] = new_delim;
        split[index][watch + 1] = 0;
    }
    split[index] = NULL;

    free(str_count);
    free(array);
    return (split);
}

here is _unsetenv with main:

   /**
    * _unsetenv - removes an environment variable.
    * @name: variable to be removed.
    *
    * Return: zero on  success,  or -1  on  error.
    */
    int _unsetenv(const char *name)
    {
        char *map, *to_search, **split, **temp_split;
        char *shell, *home, *filename;
        size_t ln;
        int s_size, **f_count, occurrence, i = 0;
        int ls, lh, f_size, f_d, lines, j;
        struct stat st;
        FILE *stream;
    
        map = NULL;
        split = NULL;
    
        ln = strlen(name);
    
        /* checking for errors */
        if (!name || !ln || strchr(name, '='))
        {
            errno = EINVAL;
            return (-1);
        }
    
        /* creating variable to search for */
        s_size = 7 + ln + 2;
        to_search = malloc(sizeof(char) * s_size);
        if (!to_search)
            return (-1);
        strcpy(to_search, "export ");
        strcat(to_search, name);
        strcat(to_search, "=");
    
        /* creating filename */
        home = getenv("HOME");
    
        lh = strlen(home);
    
        /* getting shell name */
        shell = getenv("SHELL");
        shell = &shell[5];
        ls = strlen(shell);
    
        /* creating file to open */
    
        f_size = lh + 2 + ls + 3;
        filename = malloc(sizeof(char) * f_size);
        if (!filename)
            return (-1);
    
        strcpy(filename, home);
        strcat(filename, "/.");
        strcat(filename, shell);
        strcat(filename, "rc");
    
        /* searching for variable */
        f_count = file_count(filename, to_search);
        occurrence = f_count[1][0];
        lines = **f_count;
    
        temp_split = malloc(sizeof(char) * lines - 1);
        if (!temp_split)
            return (0);
    
    
        /* if variable doesn't exist, return success */
        if (!occurrence)
            return (0);
    
        /* else remove it */
        else
        {
            /* open filename */
            stream = fopen(filename, "r+");
            if (!stream)
                return (-1);
    
            f_d = stream->_fileno;
    
            if (fstat(f_d, &st) == -1)
                return (-1);
    
            map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, f_d, 0);
    
            /* splitting string to edit file */
            split = strsplt(map, "\n", '\n');
    
            free(split[occurrence - 1]);
            split[occurrence - 1] = NULL;
    
            for (j = 0; j < lines - 1; j++, i++)
            {
                if (j == occurrence - 1)
                    i++;
    
                temp_split[j] = split[i];
    
                if (fputs(temp_split[j], stream) == EOF)
                    return (-1);
    
                printf("temp_split[%d]: %s\n", j, temp_split[j]);
            }
    
            return (0);
        }
    
        return (-1);
    }
    
    /**
     * main - tests _unsetenv().
     * @argc: argument count.
     * argv: argument vector.
     *
     * Return: 0.
     */
    int main(void)
    {
        int i;
        char *name = "FOO";
    
        if ((i = _unsetenv(name)) == -1)
            printf("%s not removed\n", name);
    
        else
            printf("%s removed\n", name);
    
        return (0);
    }
Marlon
  • 90
  • 6
  • 4
    It's not adding anything. You have a file with N bytes and you're overwriting the first M bytes, where M < N. The rest isn't touched. – Shawn Jun 02 '22 at 07:48
  • 5
    If you want to remove something from a file, write out a new temporary file with the new contents and then rename it to the original. – Shawn Jun 02 '22 at 07:49
  • Aside: [What are the rules about using an underscore in a C identifier?](https://stackoverflow.com/q/69084726/2505965) – Oka Jun 02 '22 at 08:24
  • I don't understand what you mean by its not adding anything because it writes all the strings in split to .bashrc, except when its done, it adds extra content to the last line of the file – Marlon Jun 02 '22 at 08:26
  • 2
    @Marlon No, it's not adding any extra content; it's overwriting the first part of the file, but leaving the previous content *after* what it's written. In your example above, the original file contained 134 bytes (numbered 0 through 133). Your program overwrote bytes #0 through #118, but bytes #119 through #133 still contain the last 15 bytes of the original file (which happen to be "SOME='CHANGED'" and a newline). Editing files in place is tricky (this is one of many things you can get wrong), and as Shawn said, it's better to create a new temporary file and then rename it over the original. – Gordon Davisson Jun 02 '22 at 08:47
  • okay now I get it, for a permanent change, create a temporary space to make changes then make that space permanent.Thanks guys – Marlon Jun 02 '22 at 09:32

0 Answers0