setenv, and getenv documentation

9.8k views Asked by At

I know this is a bit off topic for what is suppose to be posted on stack overflow but wanted to know if these examples are well done.

setenv:

man page - setenv

includes: #include <stdlib.h>

declaration: int setenv(const char *v_name, const char *v_value, int overwrite);

returns: If successful returns zero, otherwise returns -1, with errno set to indicate the cause of the error.

getenv:

man page - getenv

includes: #include <stdlib.h>

declaration: char *getenv(const char *name)

returns: If successful returns a pointer to the value in the environment, or NULL if there is no match.

Examples

char *ppath is used as a variable in the following examples.

Example 1: This example shows what happens when the overwrite parameter is a non-zero and v_name has a value.

ppath = getenv("PWD");                  //puts the environment of $PWD into ppath
if(ppath == NULL)                       //error checking
  perror("getenv");   
cout << "$PWD = " << ppath << endl;     //prints the environment of $PWD 

Output of this code is $PWD = /class/classes/username/CS100 this is because the char variable ppath is getting the environment from getenv("PWD") and the environment of $PWD is /class/classes/username/CS100. Therefore ppath points to that environment. There is also proper error checking to make sure that $PWD has a environment and if it does not perror is called.

ppath = getenv("HOME");                 //gets the environment of the $HOME
if(ppath == NULL)                       //error checking
  perror("getenv");
cout << "$HOME = " << ppath << endl;    //prints the environment of $PWD

Output of this code is $HOME = /class/classes/username this is because the char variable ppath is getting the environment from getenv("HOME") and the environment of $HOME is /class/classes/username. Therefore ppath points to that environment. There is also proper error checking to make sure that $HOME has a environment and if it does not perror is called.

if(-1==setenv("PWD",ppath,1))           //since the overwrite parameter is non-zero it replaces environment 
  perror("setenv");                     //of $PWD with the value of ppath which is defined by the environment 
                                        //of $HOME

This is to change the environment of $PWD in this case it take the value that the ppath is pointing to which in this case is /class/classes/username which is also $HOME. The overwrite parameter is non-zero, so it replaces the environment of $PWD to ppath

ppath = getenv("PWD");                  //gets the environment of $PWD
if(ppath == NULL)                       //error checking
  perror("getenv");
cout << "$PWD = " << ppath << endl;     //the value should now be the same as the value of $HOME

Output of this code is $PWD = /class/classes/username this is because the char variable ppath is getting the environment from getenv("PWD") and the environment of $PWD is /class/classes/username. Therefore ppath points to that environment. There is also proper error checking to make sure that $PWD has a environment and if it does not perror is called. The full output of example 1 is as follows.

Output 1:

$PWD = /class/classes/username/CS100
$HOME = /class/classes/username
$PWD = /class/classes/username

Example 2: This example shows what happens when the overwrite parameter is a non-zero and v_name has a blank value as in v_name = "" so an empty string.

ppath = getenv("PWD");                  
if(ppath == NULL)
  perror("getenv");
cout << "$PWD = " << ppath  << endl;    //in this case ppath ="" because the environment of $PWD is not set

Output of the following code will be $PWD = this is because the environment of $PWD is set to "" in this case, it makes the ppath point to an empty string. Hence the reason it does not throw an error, however if $PWD was an undefined variable then perror is called.

ppath = getenv("HOME"); 
if(ppath == NULL)
  perror("getenv");
cout << "$HOME = " << ppath << endl;

Output: of this code is $HOME = /class/classes/username this is because the char variable ppath is getting the environment from getenv("HOME") and the environment of $HOME is /class/classes/username. Therefore ppath points to that environment. There is also proper error checking to make sure that $HOME has an environment and if it does not perror is called.

if(-1==setenv("PWD",ppath,1))           //since the overwrite parameter is non-zero it replaces environment 
  perror("setenv");                     //of $PWD with the value of ppath which is defined by the environment 
                                        //of $HOME 

Since the overwrite parameter is 1 it does not matter what the $PWD it is set to ppath as defined in the earlier example.

ppath = getenv("PWD");                  
if(ppath == NULL)
  perror("getenv");  
cout << "$PWD = " << ppath << endl;     //the value should now be the same as the value of $HOME

The output of the following code is $PWD = /class/classes/username this is because setenv changed the value of $PWD as defined in the past code block, to ppath.

Output 2:

$PWD =
$HOME = /class/classes/username
$PWD = /class/classes/username

Example 3: This example shows what happens when the overwrite parameter is a zero and v_name does have a value.

ppath = getenv("PWD"); 
if(ppath == NULL)
  perror("getenv");
cout << "$PWD = " << ppath << endl;

In this case ppath is set to the environment of $PWD. The output of this code block is $PWD = /class/classes/username/CS100.

ppath = getenv("HOME"); 
if(ppath == NULL)
  perror("getenv");
cout << "$HOME = " << ppath << endl;

In this code block ppath is set to the environment of $HOME. The output of the following code block will be $HOME = /class/classes/username.

if(-1==setenv("PWD",ppath,0))           //since the overwrite parameter is zero it does not replaces  
  perror("setenv");                     //environment of $PWD with ppath.

Here there is a new case where the overwrite parameter is zero. In this case it does not matter what ppath is because the zero flag makes it so that the $PWD is not replaced by the ppath, unless $PWD was a undefined variable, in which case $PWD would be given the value of ppath.

ppath = getenv("PWD");                
if(ppath == NULL)
  perror("getenv");
cout << "$PWD = " << ppath << endl;     //the value should not be changed.

The output of this code block is $PWD = /class/classes/username/CS100. This is because setenv did not change the environment of $PWD because of the overwrite parameter.

Output 3:

$PWD = /class/classes/username/CS100
$HOME = /class/classes/username
$PWD = /class/classes/username/CS100

Example 4: This example shows what happens when the overwrite parameter is a zero or a non-zero and v_name is a parameter that is not defined in the environment.

ppath = getenv("HOME"); 
if(ppath == NULL)
  perror("getenv");
cout << "$HOME = " << ppath << endl;

In this case ppath is given the environment of $HOME, there is also proper error checking done. The output of the code block is $HOME = /class/classes/username.

if(-1==setenv("random_name",ppath,0))   //since the overwrite parameter is zero and the variable     
perror("setenv");                       //$random_name is undefined, setenv makes the environment of 
                                        //$random_variable is the value of ppath. If the case where
                                        //there is a undefined variable the setenv behaves the
                                        //same way regardless of a non-zero or zero overwrites parameter. 
                                        

In this case the overwrite parameter is still zero, but the v_name is not declared so this is where the overwrite parameter of zero is helpful. In this case random_name is set to ppath which is pointing to the environment of $HOME which is /class/classes/username.

ppath = getenv("random_name");                  //gets the value of $PWD
if(ppath == NULL)
  perror("getenv");
cout << "$random_name = " << ppath << endl;     //the value should now be the same as the value of $HOME

The ppath is contains the environment of $random_name which is set by the setenv in the previous code block. The output of this code block is $random_name = /class/classes/username

Output 4:

$HOME = /class/classes/username
$random_name = /class/classes/username
1

There are 1 answers

0
Jonathan Leffler On

You are using C++; you can declare variables easily as you write. Indeed, both C99 and C11 also allow you to declare variables when they're used.

Example 1

Example 1: This example shows what happens when the overwrite parameter is a non-zero and v_name has a value.

Actually, it is very hard to spot the code in Example 1 that has anything to do with the overwrite parameter.

ppath = getenv("PWD");                  //puts the environment of $PWD into ppath
if(ppath == NULL)                       //error checking
  perror("getenv");   
cout << "$PWD = " << ppath << endl;     //prints the environment of $PWD

You shouldn't use ppath when you've established that it is NULL; this is a pervasive problem. And these days, shouldn't you use nullptr since this is C++? I think this could be:

const char *ppath = getenv("PWD");
if (ppath == nullptr)
    perror("getenv");   
else
    cout << "$PWD = " << ppath << endl;

If you state that you're coding for backwards compatibility, I've no major problem with using NULL or 0 instead of nullptr.

In fact, you have the almost identical block of code three times; you need to write a function:

void show_env(const char *varname)
{
    const char *ppath = getenv(varname);
    if (ppath == nullptr)
        perror("getenv");   
    else
        cout << "$" << varname << " = " << ppath << endl;
}

This is a direct transliteration of the original code. It also shows why perror() is a bad function; it is hard to make the error message meaningful (it won't include the variable name). Add to that the fact that the POSIX specification for getenv() does not specify that errno is set by getenv(), and we should revise the code to:

void show_env(const char *varname)
{
    const char *ppath = getenv(varname);
    if (ppath == nullptr)
        cerr << "No value in the environment for $" << varname << endl;   
    else
        cout << "$" << varname << " = " << ppath << endl;
}

You can then call:

show_env("PWD");
show_env("HOME");
…code using setenv()…
show_env("PWD");

Etc. I'm not convinced that the code referencing $HOME is relevant; you really don't use it.

Personally, I dislike (intensely) the back-to-front comparison style you use in the code for setenv(). When Yoda I want like to speak…

Note that setting the environment variable $PWD alone does not change the current working directory.

Example 2

Your example 2 does not clearly show the v_name set to an empty string. However, that is a case covered by the POSIX specification for setenv(). This time, an error is defined (errno will be set to EINVAL).

There is so much repeated code, again, it is hard to spot what's interesting.

Example 3

You say:

Example 3: This example shows what happens when the overwrite parameter is a zero and v_name does have a value.

I think you mean:

Example 3: This example shows what happens when the overwrite parameter is a zero and the environment variable identified by v_name does have a value.

This is probably applicable previously, too.

Example 4

You say:

ppath = getenv("random_name");                  //gets the value of $PWD

The comment doesn't match the code.

Style on SO

You should make a heading for each example with ### as the line prefix. You might or might not have a super-heading ## Examples. You might mimic the style of a manual page for the function outline, or you might simply omit everything except the synopsis, referring to where the function behaviour is defined.

As written, it is extremely hard to find the 4 examples (it was only late in the process of answering that I realized there were four examples rather than three). You have a lot of repetition. You also have a good deal of noise (the code referencing $HOME is basically noise.