env: allow bare --env0-from to round-trip

Support --env0-from reading any environment generated by -0,
irrespective of whether -i is specified.

* src/env.c (set_envvars_from_file): Now that this function
always builds its own vector, support reading entries
without an equals, which are just appended to the existing vector.
* doc/coreutils.texi (env invocation): Adjust accordingly.
* tests/env/env0-from.sh: Likewise.
This commit is contained in:
Pádraig Brady committed 2026-08-14 18:00:15 +01:00
1 parent 9c28113d8a
commit e871648b79
3 files changed
+46 -23

No files matched your search

+5 -4
View File
@@ -17573,10 +17573,11 @@ Read NUL-terminated environment entries from @var{file}. If @var{file}
is @samp{-}, read from standard input. The file must either be empty or
end with a NUL byte.
Without @option{--ignore-environment}, each entry must be a
@samp{@var{name}=@var{value}} assignment. Assignments replace inherited
variables with the same name, and a later assignment in @var{file} takes
precedence over an earlier one.
Without @option{--ignore-environment}, entries of the form
@samp{@var{name}=@var{value}} replace inherited variables with the same
name, and a later assignment in @var{file} takes precedence over an earlier
one. Entries without an @samp{=} byte, including empty entries, are
appended to the environment without replacing another entry.
With @option{--ignore-environment}, the entries replace the environment
directly. Their order and contents are preserved, including duplicate
+19 -14
View File
@@ -346,7 +346,7 @@ read_env_file (char const *file)
return size;
}
/* Merge the NUL-delimited assignments in FILE into the environment.
/* Merge the NUL-delimited entries in FILE into the environment.
Return true if ENV now owns the environment vector. */
static bool
set_envvars_from_file (struct env_vector *env, char const *file)
@@ -380,10 +380,6 @@ set_envvars_from_file (struct env_vector *env, char const *file)
for (char *assignment = env0_from_buffer; assignment < end; )
{
char *next = assignment + strlen (assignment) + 1;
if (! strchr (assignment, '='))
error (EXIT_CANCELED, 0,
_("invalid variable specification %s in %s"),
quoteaf_n (0, assignment), quoteaf_n (1, file));
++file_count;
assignment = next;
}
@@ -424,17 +420,26 @@ set_envvars_from_file (struct env_vector *env, char const *file)
for (char *assignment = env0_from_buffer; assignment < end; )
{
char *next = assignment + strlen (assignment) + 1;
devmsg ("setenv: %s\n", assignment);
char **slot = hash_lookup (slot_table, &assignment);
if (slot)
*slot = assignment;
char *eq = strchr (assignment, '=');
if (! eq)
{
devmsg ("append: %s\n", assignment);
merged[merged_count++] = assignment;
}
else
{
merged[merged_count] = assignment;
if (! hash_insert (slot_table, &merged[merged_count]))
xalloc_die ();
++merged_count;
devmsg ("setenv: %s\n", assignment);
char **slot = hash_lookup (slot_table, &assignment);
if (slot)
*slot = assignment;
else
{
merged[merged_count] = assignment;
if (! hash_insert (slot_table, &merged[merged_count]))
xalloc_die ();
++merged_count;
}
}
assignment = next;
}
+22 -5
View File
@@ -148,13 +148,30 @@ printf %s '' >empty || framework_failure_
env -i --env0-from=empty -0 >out || fail=1
compare /dev/null out || fail=1
# The file must end in NUL. Without -i, every record is an assignment.
# Without -i, append entries that have no name to look up. Native Windows
# rejects such entries since its environment cannot represent them.
case $host_os in
mingw* | windows*)
printf 'not-an-assignment\0' >invalid || framework_failure_
returns_ 125 env --env0-from=invalid >out 2>err || fail=1
printf '\0' >invalid || framework_failure_
returns_ 125 env --env0-from=invalid >out 2>err || fail=1
;;
*)
printf 'A=file1\0opaque\0\0A=file2\0B=new\0' >mixed \
|| framework_failure_
printf 'A=file2\0opaque\0\0B=new\0' >exp || framework_failure_
LC_ALL=C sort -z exp >exp-sorted || framework_failure_
env -i A=old "$abs_top_builddir/src/env$EXEEXT" \
--env0-from=mixed -0 >all || fail=1
LC_ALL=C sort -z all >out || framework_failure_
compare exp-sorted out || fail=1
;;
esac
# The file must end in NUL.
printf 'A=unterminated' >invalid || framework_failure_
returns_ 125 env --env0-from=invalid >out 2>err || fail=1
printf 'not-an-assignment\0' >invalid || framework_failure_
returns_ 125 env --env0-from=invalid >out 2>err || fail=1
printf '\0' >invalid || framework_failure_
returns_ 125 env --env0-from=invalid >out 2>err || fail=1
returns_ 125 env --env0-from=does-not-exist >out 2>err || fail=1
# Invalid -u names are still diagnosed after loading variables from a file.