Skip to content

Commit 38037e8

Browse files
committedMay 23, 2023
Preserve shell options
Closes #28.
1 parent 1941b7c commit 38037e8

File tree

3 files changed

+65
-15
lines changed

3 files changed

+65
-15
lines changed
 

‎src/main.rs

+55-14
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::ffi::{OsStr, OsString};
1111
use std::fs::{read, read_link, File};
1212
use std::io::{Read, Write};
1313
use std::os::unix::ffi::OsStrExt;
14+
use std::os::unix::prelude::OsStringExt;
1415
use std::os::unix::process::CommandExt;
1516
use std::os::unix::process::ExitStatusExt;
1617
use std::path::{Path, PathBuf};
@@ -28,6 +29,12 @@ mod trace;
2829

2930
type EnvMap = BTreeMap<OsString, OsString>;
3031

32+
struct EnvOptions {
33+
env: EnvMap,
34+
bashopts: OsString,
35+
shellopts: OsString,
36+
}
37+
3138
static XDG_DIRS: Lazy<xdg::BaseDirectories> = Lazy::new(|| {
3239
xdg::BaseDirectories::with_prefix("cached-nix-shell")
3340
.expect("Can't get find base cache directory")
@@ -216,7 +223,7 @@ fn run_nix_shell(inp: &NixShellInput) -> NixShellOutput {
216223

217224
let env_file = NamedTempFile::new().expect("can't create temporary file");
218225
let env_cmd = [
219-
b"env -0 > ",
226+
b"{ printf \"BASHOPTS=%s\\0SHELLOPTS=%s\\0\" \"${BASHOPTS-}\" \"${SHELLOPTS-}\" ; env -0; } >",
220227
bash::quote(env_file.path().as_os_str().as_bytes()).as_slice(),
221228
]
222229
.concat();
@@ -327,7 +334,7 @@ fn run_script(
327334
.arg(fname)
328335
.args(script_args)
329336
.env_clear()
330-
.envs(&env)
337+
.envs(&env.env)
331338
.exec()
332339
} else {
333340
// eprintln!("Interpreter is bash command, executing 'bash -c'");
@@ -342,7 +349,7 @@ fn run_script(
342349
.arg(fname)
343350
.args(script_args)
344351
.env_clear()
345-
.envs(&env)
352+
.envs(&env.env)
346353
.exec()
347354
};
348355

@@ -409,25 +416,57 @@ fn run_from_args(args: Vec<OsString>) {
409416
let inp = args_to_inp(nix_shell_pwd, &args);
410417
let env = cached_shell_env(args.pure, &inp);
411418

419+
let mut bash_args = Vec::new();
420+
// XXX: only check for options that are set by current stdenv and nix-shell.
421+
env.bashopts
422+
.as_bytes()
423+
.split(|&b| b == b':')
424+
.filter(|opt| {
425+
[b"execfail".as_ref(), b"inherit_errexit", b"nullglob"]
426+
.contains(opt)
427+
})
428+
.for_each(|opt| {
429+
bash_args.extend_from_slice(&[
430+
"-O".into(),
431+
OsString::from_vec(opt.to_vec()),
432+
])
433+
});
434+
env.shellopts
435+
.as_bytes()
436+
.split(|&b| b == b':')
437+
.filter(|opt| [b"pipefail".as_ref()].contains(opt))
438+
.for_each(|opt| {
439+
bash_args.extend_from_slice(&[
440+
"-o".into(),
441+
OsString::from_vec(opt.to_vec()),
442+
])
443+
});
444+
412445
let (cmd, cmd_args) = match args.run {
413-
args::RunMode::InteractiveShell => (
414-
"bash".into(),
415-
vec!["--rcfile".into(), env!("CNS_RCFILE").into()],
416-
),
417-
args::RunMode::Shell(cmd) => ("bash".into(), vec!["-c".into(), cmd]),
446+
args::RunMode::InteractiveShell => {
447+
bash_args.extend_from_slice(&[
448+
"--rcfile".into(),
449+
env!("CNS_RCFILE").into(),
450+
]);
451+
("bash".into(), bash_args)
452+
}
453+
args::RunMode::Shell(cmd) => {
454+
bash_args.extend_from_slice(&["-c".into(), cmd]);
455+
("bash".into(), bash_args)
456+
}
418457
args::RunMode::Exec(cmd, cmd_args) => (cmd, cmd_args),
419458
};
420459

421460
let exec = Command::new(cmd)
422461
.args(cmd_args)
423462
.env_clear()
424-
.envs(&env)
463+
.envs(&env.env)
425464
.exec();
426465
eprintln!("cached-nix-shell: couldn't run: {:?}", exec);
427466
exit(1);
428467
}
429468

430-
fn cached_shell_env(pure: bool, inp: &NixShellInput) -> EnvMap {
469+
fn cached_shell_env(pure: bool, inp: &NixShellInput) -> EnvOptions {
431470
let inputs = serialize_vecs(&[
432471
&serialize_env(&inp.env),
433472
&serialize_args(&inp.args),
@@ -453,12 +492,14 @@ fn cached_shell_env(pure: bool, inp: &NixShellInput) -> EnvMap {
453492
outp.env
454493
};
455494

495+
let shellopts = env.remove(OsStr::new("SHELLOPTS")).unwrap_or_default();
496+
let bashopts = env.remove(OsStr::new("BASHOPTS")).unwrap_or_default();
456497
env.insert(OsString::from("IN_CACHED_NIX_SHELL"), OsString::from("1"));
457498

458-
if pure {
459-
env
460-
} else {
461-
merge_env(env)
499+
EnvOptions {
500+
env: if pure { env } else { merge_env(env) },
501+
shellopts,
502+
bashopts,
462503
}
463504
}
464505

‎tests/run.sh

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
set -e
44

55
# Check prerequisites
6-
hash cached-nix-shell cat chmod cp date grep ln mkdir rm tail tee time touch
6+
hash cached-nix-shell cat chmod cmp cp \
7+
date diff env grep ln mkdir rev rm tail tee time touch
78

89
trap 'exit 130' INT
910

‎tests/t16-shopt.sh

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/bin/sh
2+
. ./lib.sh
3+
# Check that shell options are preserved.
4+
# https://github.com/xzfc/cached-nix-shell/issues/28
5+
6+
run nix-shell --pure -p --run '{ shopt -p; shopt -po; } > tmp/nix-shell'
7+
run cached-nix-shell --pure -p --run '{ shopt -p; shopt -po; } > tmp/cached-nix-shell'
8+
check "Options are the same" diff tmp/nix-shell tmp/cached-nix-shell

0 commit comments

Comments
 (0)