Re: [PATCH v3 1/5] wrapper.c: add x{un,}setenv(), and use xsetenv() in environment.c
- From
Taylor Blau <me@ttaylorr.com>
- Date
- Sep 20, 2021, 21:53 UTC
- Message-ID
- <YUkC7RIDR7pQWsPh@nand.local>
- In-Reply-To
- <patch-v3-1.5-4b320edc933-20210919T084703Z-avarab@gmail.com>
On Sun, Sep 19, 2021 at 10:47:15AM +0200, Ævar Arnfjörð Bjarmason wrote:
Show 7 quoted lines
> Add fatal wrappers for setenv() and unsetenv(). In d7ac12b25d3 (Add > set_git_dir() function, 2007-08-01) we started checking its return > value, and since 48988c4d0c3 (set_git_dir: die when setenv() fails, > 2018-03-30) we've had set_git_dir_1() die if we couldn't set it. > > Let's provide a wrapper for both, this will be useful in many other > places, a subsequent patch will make another use of xsetenv().
Makes sense.
Show 6 quoted lines
> We could make these return "void" (as far as I can tell there's no > other x*() wrappers that needed to make that decision before), > i.e. our "return 0" is only to indicate that we didn't error, which we > would have died on. Let's return "int" instead to be consistent with > the C library function signatures, including for any future code that > expects a pointer to a setenv()-like function.
This may be a little over-clever ;). It is cute, but returning an int makes xsetenv a drop-in replacement for setenv. Which is nice, but it makes it all too-easy to take code like:
if (setenv(...) < 0)
die(_("..."));and replace it with
if (xsetenv(...) < 0)
which makes the whole conditional redundant, since the wrappers are guaranteed not to return an error.
In other words, I like the idea that s/setenv/x&/ causes a compile-time error, and returning an int from these wrappers prevents that from happening.
This may be a little too-theoretical, and you're certainly free to disagree, just my $0.02.
Show 8 quoted lines
> I think it would be OK skip the NULL check of the "name" here for the > calls to die_errno(). Almost all of our setenv() callers are taking a > constant string hardcoded in the source as the first argument, and for > the rest we can probably assume they've done the NULL check > themselves. Even if they didn't, modern C libraries are forgiving > about it (e.g. glibc formatting it as "(null)"), on those that aren't, > well, we were about to die anyway. But let's include the check anyway > for good measure.
This I think is a good call. I agree in practice that most times we'd be just fine to pass null to printf() (as we have seen from 88617d11f9 (multi-pack-index: fix potential segfault without sub-command, 2021-07-19) ;)). But there's no reason to rely on risky assumptions when it's easy to avoid doing so.
Show 23 quoted lines
> diff --git a/wrapper.c b/wrapper.c
> index 7c6586af321..95f989260cd 100644
> --- a/wrapper.c
> +++ b/wrapper.c
> @@ -145,6 +145,21 @@ void *xcalloc(size_t nmemb, size_t size)
> return ret;
> }
>
> +int xsetenv(const char *name, const char *value, int overwrite)
> +{
> + if (setenv(name, value, overwrite))
> + die_errno("setenv(%s, '%s', %d) failed", name ? name : "(null)",
> + value, overwrite);
> + return 0;
> +}
> +
> +int xunsetenv(const char *name)
> +{
> + if (!unsetenv(name))
> + die_errno("unsetenv(%s) failed", name ? name : "(null)");
> + return 0;
> +}
> +For what it's worth, I find these new messages a little wordy. Maybe we should just sticky "could not (un)set %s"?
Thanks, Taylor