Handle old versions of `/usr/bin/env` when loading shell env (#10202)

Thorsten Ball and Dave Smith created

This fixes #9786 by using an invocation of `/usr/bin/env` that's
supported by macOS 12.

As it turns out, on macOS 12 (and maybe 13?) `/usr/bin/env` doesn't
support the `-0` flag. In our case it would silently fail, since we
`exit 0` in our shell invocation and because the program we run and
whose exit code we check is the `$SHELL` and not `/usr/bin/env`.

What this change does is to drop the `-0` and instead split the
environment on `\n`. This works even if an environment variable contains
a newline character because that would then be escaped.

Release Notes:

- Fixed Zed not picking up shell environments correctly when running on
macOS 12. ([#9786](https://github.com/zed-industries/zed/issues/9786)).

Co-authored-by: Dave Smith <davesmithsemail@gmail.com>

Change summary

crates/project/src/project.rs | 5 +++--
crates/zed/src/main.rs        | 4 ++--
2 files changed, 5 insertions(+), 4 deletions(-)

Detailed changes

crates/project/src/project.rs 🔗

@@ -10522,7 +10522,7 @@ async fn load_shell_environment(dir: &Path) -> Result<HashMap<String, String>> {
         });
 
     let command = format!(
-        "cd '{}';{} printf '%s' {marker}; /usr/bin/env -0; exit 0;",
+        "cd '{}';{} printf '%s' {marker}; /usr/bin/env; exit 0;",
         dir.display(),
         additional_command.unwrap_or("")
     );
@@ -10549,13 +10549,14 @@ async fn load_shell_environment(dir: &Path) -> Result<HashMap<String, String>> {
 
     let mut parsed_env = HashMap::default();
     let env_output = &stdout[env_output_start + marker.len()..];
-    for line in env_output.split_terminator('\0') {
+    for line in env_output.split_terminator('\n') {
         if let Some(separator_index) = line.find('=') {
             let key = line[..separator_index].to_string();
             let value = line[separator_index + 1..].to_string();
             parsed_env.insert(key, value);
         }
     }
+
     Ok(parsed_env)
 }
 

crates/zed/src/main.rs 🔗

@@ -906,7 +906,7 @@ async fn load_login_shell_environment() -> Result<()> {
     // We still don't know why `$SHELL -l -i -c '/usr/bin/env -0'`  would
     // do that, but it does, and `exit 0` helps.
     let shell_cmd = format!(
-        "{}printf '%s' {marker}; /usr/bin/env -0; exit 0;",
+        "{}printf '%s' {marker}; /usr/bin/env; exit 0;",
         shell_cmd_prefix.as_deref().unwrap_or("")
     );
 
@@ -923,7 +923,7 @@ async fn load_login_shell_environment() -> Result<()> {
 
     if let Some(env_output_start) = stdout.find(marker) {
         let env_output = &stdout[env_output_start + marker.len()..];
-        for line in env_output.split_terminator('\0') {
+        for line in env_output.split_terminator('\n') {
             if let Some(separator_index) = line.find('=') {
                 let key = &line[..separator_index];
                 let value = &line[separator_index + 1..];