This is the mail archive of the
newlib@sourceware.org
mailing list for the newlib project.
Re: How to use a static environment
Il 05/04/2018 18:34, Bob Dunlop ha scritto:
On Thu, Apr 05 at 05:46, Giuseppe Modugno wrote:
Il 05/04/2018 17:32, Bob Dunlop ha scritto:
Hi,
I don't know the newlib specifics but have played with this in other
library environments before.
The answer to your first question "Should unsetenv() free?" the
answer is No because unsetenv() doesn't know where the original string
came from. You could be freeing something that was never malloc'd.
setenv() calls malloc to copy and save the new variable/value pair in a
single string. So I was thinking the behaviour of unsetenv() should be
symmetrical, i.e. unsetenv() should have free. Otherwise:
? setenv("TZ", "whatever", 1);
? unsetenv("TZ");
and you will have a memory leak... right?
Yes you have a memory leak. It's even documented in the Glibc putenv man
page, you either accept a memory leak or violate SUSv2.
From the online glibc reference manual, I read:
The difference to the setenv function is that the *exact string given
as the parameter string is put into the environment*. If the user
should change the string after the putenv call this will reflect
automatically in the environment. This also requires that string not
be an automatic variable whose scope is left before the variable is
removed from the environment. The same applies of course to
dynamically allocated variables which are freed later.
I don't think putenv() implementation of newlib behaves in the same way.
putenv() calls setenv(), so they are identical.
IMHO it is better the behaviour of glibc putenv() (I don't know if it is
a standard behaviour), because it would avoid dynamic allocation (that
must be avoided in some circumstances). Moreover, in my case I could
safely change the string "TZ=<value>" at a later time, without calling
again putenv() (and this is explicitly specified in the glibc manual).
Also as I said before you don't have a clue where the original environment
value came from. In my early Unix days exec() set it up on the top of stack
like it was auto variables in the function that called main(). Think I saw
a system once that passed it as a special data segment/page. I've certainly
seen embedded systems where the initial values were in ROM and the pointer
array built on the fly, pointing at the ROM until changes were made.
In glibc, if you call only putenv() and unsetenv(), you have full
control of environment variables without memory leaks:
char *tz_env = malloc(100);
strpcy(tz_env, "TZ=<timezone1>");
putenv(tz_env);
tzset();
...
strcpy(tz_env, "TZ=<timezone2>2);
/* No need to call putenv() again, because environment points exactly
to tz_env */
tzset();
...
unsetenv("TZ");
free(tz_env);
In this case you don't have any memory leak.
Even in glibc, you will have a memory leak if you call unsetenv() after
sentev().
Problem is to find the lowest common denominator of the different libraries
and platforms. How about:
static char tzbuf[] = "TZ=Value_long_enough_for_any_we_want_to_set";
char *tzbuf_value;
/* Need to clear any previous string as we don't know where it's been.
* This is probably a one off memory leak.
*/
unsetenv("TZ");
/* Set our buffer although library may take a malloc'd copy */
setenv( tzbuf );
/* Find the value buffer location (either our original or the copy) */
tzbuf_value = getenv("TZ");
strcpy( tzbuf_value, "UTC0" );
etc...
It is tricky, but it's a possible solution. Anyway IMHO it's better to
implement putenv() to use the string passed as argument without
duplicating it.
Regading putenv(), I read the following code:
int
_DEFUN (_putenv_r, (reent_ptr, str),
struct _reent *reent_ptr _AND
char *str)
{
register char *p, *equal;
int rval;
p = _strdup_r (reent_ptr, str);
if (!p)
return 1;
if (!(equal = strchr (p, '=')))
{
(void) _free_r (reent_ptr, p);
return 1;
}
*equal = '\0';
rval = _setenv_r (reent_ptr, p, equal + 1, 1);
(void) _free_r (reent_ptr, p);
return rval;
}
I don't explain why the check for the presence of equal sign is after
calling strdup (so we need to call free). IMHO it would be better to
make the check immediately.
Should work unless you have a system that stores the environment as a single
buffer "key1\0value1\0key2\0value2\0\0" and shuffles the buffer up and down
as you expand/contract the individual values. A 1980's embedded system if
I recall correctly.