The operation of these three functions is as follows.
The putenv function takes a string of the form
name=value and places it in the environment list. If
name already exists, its old definition is first removed.
The setenv function sets
name to
value . If
name already exists in the environment, then (a) if
rewrite is nonzero, the existing definition for
name is first removed; (b) if
rewrite is 0, an existing definition for
name is not removed,
name is not set to the new
value , and no error occurs.
The unsetenv function removes any definition of
name . It is not an error if such a definition does not exist.
Note the difference between putenv and setenv. Whereas setenv must allocate memory to create the
name=value string from its arguments, putenv is free to place the string passed to it directly into the environment. Indeed, on Linux and Solaris, the putenv implementation places the address of the string we pass to it directly into the environment list. In this case, it would be an error to pass it a string allocated on the stack, since the memory would be reused after we return from the current function.
It is interesting to examine how these functions must operate when modifying the environment list. Recall Figure 7.6: the environment listthe array of pointers to the actual
name=value stringsand the environment strings are typically stored at the top of a process's memory space, above the stack. Deleting a string is simple; we simply find the pointer in the environment list and move all subsequent pointers down one. But adding a string or modifying an existing string is more difficult. The space at the Figure 7.6 that if the original environment list was contained above the top of the stack, as is common, then we have moved this list of pointers to the heap. But most of the pointers in this list still point to
name=value strings above the top of the stack.
If this isn't the first time we've added new strings to the environment list, then we know that we've already allocated room for the list on the heap, so we just call realloc to allocate room for one more pointer. The pointer to the new
name=value string is stored at the end of the list (on top of the previous null pointer), followed by a null pointer.