Repository: alacritty/alacritty
File: alacritty_terminal/src/tty/unix.rs (Rust)

Code before:
    pub fn file(&self) -> &File {
        &self.file
    }
}

fn shell_command(shell_config: Option<&Program>, pw: &Passwd<'_>) -> Command {
    // Look for a shell in the alacritty config first, then SHELL, then in passwd.
    let shell = shell_config
        .map(|prog| prog.program().to_owned())
        .or_else(|| env::var("SHELL").ok())
        .unwrap_or_else(|| pw.shell.to_owned());

    #[cfg(not(target_os = "macos"))]
    let mut command = Command::new(shell);

    // On macOS, unless the user set the `shell` config in alacritty.yml, use the `login` command to
    // launch the shell so programs like `w`, `who`, and `logname` work.
    #[cfg(target_os = "macos")]
    let mut command = if shell_config.is_some() {
        Command::new(shell)
    } else {
        let mut login_command = Command::new("/usr/bin/login");
        // -f bypasses authentication for the already-logged-in user
        // -l makes it not a "login" shell and prevents login from changing directories to $HOME
        // exec the shell with argv[0] prepended by - so the shell run as a login shell anyway
        login_command.args(["-fl", pw.name, "/bin/zsh", "-c"]);
        login_command.arg(format!("exec -a -{} {}", shell.rsplit('/').next().unwrap(), shell));
        login_command
    };

    // If the user specified a shell in alacritty.yml, the arguments are added here.
    // Note that if we wanted to implicitly invoke these via login on macOS also, the arguments
    // would need to be added to the `format!("exec ...")` above, but we only take that path if
    // there is no shell config, and hence no arguments to add.
    shell_config.map(|prog| command.args(prog.args()));

    command
}

/// Create a new TTY and return a handle to interact with it.
pub fn new(config: &PtyConfig, window_size: WindowSize, window_id: Option<usize>) -> Result<Pty> {
    let (master, slave) = make_pty(window_size.to_winsize())?;

    #[cfg(any(target_os = "linux", target_os = "macos"))]
    if let Ok(mut termios) = termios::tcgetattr(master) {
        // Set character encoding to UTF-8.
        termios.input_flags.set(InputFlags::IUTF8, true);
        let _ = termios::tcsetattr(master, SetArg::TCSANOW, &termios);
    }

    let mut buf = [0; 1024];

Review comment (suggestion):
I'd say especially for older machines `zsh` is far more likely to not exist than `bash`. Sure bash might be deprecated as a login shell, but I can't imagine they'd get rid of it, breaking any script ever.

I'm not sure if `sh` is just some symlink to one of those, but it certainly would be another option.

Code after:
    pub fn file(&self) -> &File {
        &self.file
    }
}

/// Look for a shell in the `$SHELL` environment variable, then in `passwd`.
fn default_shell(pw: &Passwd<'_>) -> String {
    env::var("SHELL").unwrap_or_else(|_| pw.shell.to_owned())
}

#[cfg(not(target_os = "macos"))]
fn default_shell_command(pw: &Passwd<'_>) -> Command {
    Command::new(default_shell(pw))
}

#[cfg(target_os = "macos")]
fn default_shell_command(pw: &Passwd<'_>) -> Command {
    let shell = default_shell(pw);
    let shell_name = shell.rsplit('/').next().unwrap();

    // On macOS, use the `login` command so the shell will appear as a tty session.
    let mut login_command = Command::new("/usr/bin/login");

    // Exec the shell with argv[0] prepended by '-' so it becomes a login shell.
    // `login` normally does this itself, but `-l` disables this.
    let exec = format!("exec -a -{} {}", shell_name, shell);

    // -f: Bypasses authentication for the already-logged-in user.
    // -l: Skips changing directory to $HOME and prepending '-' to argv[0].
    // -p: Preserves the environment.
    login_command.args(["-flp", pw.name, "/bin/sh", "-c", &exec]);
    login_command
}

/// Create a new TTY and return a handle to interact with it.
pub fn new(config: &PtyConfig, window_size: WindowSize, window_id: Option<usize>) -> Result<Pty> {
    let (master, slave) = make_pty(window_size.to_winsize())?;

    #[cfg(any(target_os = "linux", target_os = "macos"))]
    if let Ok(mut termios) = termios::tcgetattr(master) {
        // Set character encoding to UTF-8.
        termios.input_flags.set(InputFlags::IUTF8, true);
        let _ = termios::tcsetattr(master, SetArg::TCSANOW, &termios);
    }

    let mut buf = [0; 1024];
    let pw = get_pw_entry(&mut buf)?;

    let mut builder = if let Some(shell) = config.shell.as_ref() {
        let mut cmd = Command::new(shell.program());
        cmd.args(shell.args());