cstrncpyenviron

Copying strings from extern char environ in C


I have a question pertaining to the extern char **environ. I'm trying to make a C program that counts the size of the environ list, copies it to an array of strings (array of array of chars), and then sorts it alphabetically with a bubble sort. It will print in name=value or value=name order depending on the format value.

I tried using strncpy to get the strings from environ to my new array, but the string values come out empty. I suspect I'm trying to use environ in a way I can't, so I'm looking for help. I've tried to look online for help, but this particular program is very limited. I cannot use system(), yet the only help I've found online tells me to make a program to make this system call. (This does not help).

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
extern char **environ;
int main(int argc, char *argv[])
{
    char **env = environ;
    int i = 0;
    int j = 0;
    printf("Hello world!\n");
    int listSZ = 0;
    char temp[1024];
    while(env[listSZ])
    {
        listSZ++;
    }
    printf("DEBUG: LIST SIZE = %d\n", listSZ);
    char **list = malloc(listSZ * sizeof(char**));
    char **sorted = malloc(listSZ * sizeof(char**));
    for(i = 0; i < listSZ; i++)
    {
        list[i] = malloc(sizeof(env[i]) * sizeof(char));        // set the 2D Array strings to size 80, for good measure
        sorted[i] = malloc(sizeof(env[i]) * sizeof(char));
    }
    while(env[i])
    {
        strncpy(list[i], env[i], sizeof(env[i]));
        i++;
    }           // copy is empty???

    for(i = 0; i < listSZ - 1; i++)
    {
        for(j = 0; j < sizeof(list[i]); j++)
        {
            if(list[i][j] > list[i+1][j])
            {
                strcpy(temp, list[i]);
                strcpy(list[i], list[i+1]);
                strcpy(list[i+1], temp);
                j = sizeof(list[i]);                    // end loop, we resolved this specific entry
            }
            // else continue
        }
    }

This is my code, help is greatly appreciated. Why is this such a hard to find topic? Is it the lack of necessity?

EDIT: Pasted wrong code, this was a separate .c file on the same topic, but I started fresh on another file.


Solution

  • To get the environment variables, you need to declare main like this:

    int main(int argc, char **argv, char **env);
    

    The third parameter is the NULL-terminated list of environment variables. See:

    #include <stdio.h>
    
    int main(int argc, char **argv, char **environ)
    {
        for(size_t i = 0; env[i]; ++i)
            puts(environ[i]);
    
        return 0;
    }
    

    The output of this is:

    LD_LIBRARY_PATH=/home/shaoran/opt/node-v6.9.4-linux-x64/lib:
    LS_COLORS=rs=0:di=01;34:ln=01;36:m
    ...
    

    Note also that sizeof(environ[i]) in your code does not get you the length of the string, it gets you the size of a pointer, so

    strncpy(list[i], environ[i], sizeof(environ[i]));
    

    is wrong. Also the whole point of strncpy is to limit based on the destination, not on the source, otherwise if the source is larger than the destination, you will still overflow the buffer. The correct call would be

    strncpy(list[i], environ[i], 80);
    list[i][79] = 0;
    

    Bare in mind that strncpy might not write the '\0'-terminating byte if the destination is not large enough, so you have to make sure to terminate the string. Also note that 79 characters might be too short for storing env variables. For example, my LS_COLORS variable is huge, at least 1500 characters long. You might want to do your list[i] = malloc calls based based on strlen(environ[i])+1.

    Another thing: your swapping

    strcpy(temp, list[i]);
    strcpy(list[i], list[i+1]);
    strcpy(list[i+1], temp);
    j = sizeof(list[i]);
    

    works only if all list[i] point to memory of the same size. Since the list[i] are pointers, the cheaper way of swapping would be by swapping the pointers instead:

    char *tmp = list[i];
    list[i] = list[i+1];
    list[i+1] = tmp;
    

    This is more efficient, is a O(1) operation and you don't have to worry if the memory spaces are not of the same size.

    What I don't get is, what do you intend with j = sizeof(list[i])? Not only that sizeof(list[i]) returns you the size of a pointer (which will be constant for all list[i]), why are you messing with the running variable j inside the block? If you want to leave the loop, the do break. And you are looking for strlen(list[i]): this will give you the length of the string.