Skip to content

Instantly share code, notes, and snippets.

@foriequal0
Last active May 12, 2026 06:47
Show Gist options
  • Select an option

  • Save foriequal0/af3d4c1542c4efc444f8a91449eaf304 to your computer and use it in GitHub Desktop.

Select an option

Save foriequal0/af3d4c1542c4efc444f8a91449eaf304 to your computer and use it in GitHub Desktop.
Resonable Rust port of `msys2_shell.cmd`
///
/// Reasonable Rust port of
/// https://github.com/msys2/MSYS2-packages/blob/master/filesystem/msys2_shell.cmd
///
use std::ffi::{OsStr, OsString};
use std::os::windows::process::CommandExt;
use std::path::{Path, PathBuf};
use std::process::{Command, ExitCode, Stdio};
fn main() -> Result<(), ExitCode> {
let mut wd = std::env::current_dir().expect("current directory");
if !wd.join("msys-2.0.dll").exists() {
wd = std::env::current_exe()
.expect("current executable")
.parent()
.expect("parent directory")
.join("usr/bin/");
}
let params = checkparams(&wd)?;
let msystem = params.msystem.as_deref();
let msyscon = params.msyscon.as_deref();
let msys2_nostart = params.msys2_nostart.clone();
let con = if msystem == Some(OsStr::new("MINGW32")) {
TitleAndIcon::new("MinGW x32", "mingw32.ico")
} else if msystem == Some(OsStr::new("MINGW64")) {
TitleAndIcon::new("MinGW x64", "mingw64.ico")
} else if msystem == Some(OsStr::new("UCRT64")) {
TitleAndIcon::new("MinGW UCRT x64", "ucrt64.ico")
} else if msystem == Some(OsStr::new("CLANG64")) {
TitleAndIcon::new("MinGW Clang x64", "clang64.ico")
} else if msystem == Some(OsStr::new("CLANGARM64")) {
TitleAndIcon::new("MinGW Clang ARM64", "clangarm64.ico")
} else {
TitleAndIcon::new("MSYS2 MSYS", "msys2.ico")
};
let command = if msyscon == Some(OsStr::new("mintty.exe")) {
startmintty(&wd, &params, &con)
} else if msyscon == Some(OsStr::new("conemu")) {
startconemu(&wd, &params, &con)?
} else if msyscon == Some(OsStr::new("defterm")) {
startsh(&wd, &params)
} else {
let mintty = wd.join("mintty.exe");
if !mintty.exists() {
startsh(&wd, &params)
} else {
startmintty(
&wd,
&Params {
msyscon: Some("mintty.exe".into()),
..params
},
&con,
)
}
};
spawn_or_start(command, msys2_nostart.as_deref())
}
fn spawn_or_start(mut command: Command, msys2_nostart: Option<&OsStr>) -> Result<(), ExitCode> {
let nostart = msys2_nostart.is_some_and(|x| !x.is_empty());
if !nostart {
// try to match the behavior of the ` start ` command in `CMD`.
// TODO: set title
// https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags#flags:~:text=behavior%2E-,CREATE%5FNEW%5FCONSOLE
const CREATE_NEW_CONSOLE: u32 = 0x0000_0010;
command.creation_flags(CREATE_NEW_CONSOLE);
if let Err(err) = command.spawn() {
eprintln!("{err}");
return Err(ExitCode::FAILURE);
}
}
match command.status() {
Ok(status) => {
if status.success() {
return Ok(());
}
if let Some(status_code) = status.code()
&& let Ok(exit_code) = u8::try_from(status_code)
{
return Err(ExitCode::from(exit_code));
}
Err(ExitCode::FAILURE)
}
Err(err) => {
eprintln!("{err}");
Err(ExitCode::FAILURE)
}
}
}
struct Params {
msystem: Option<OsString>,
msyscon: Option<OsString>,
msys2_path_type: Option<OsString>,
msys2_nostart: Option<OsString>,
chere_invoking: Option<OsString>,
where_dir: Option<PathBuf>,
loginshell: OsString,
shell_args: Vec<OsString>,
}
/// `:checkparams`
fn checkparams(wd: &Path) -> Result<Params, ExitCode> {
let mut msystem = std::env::var_os("MSYSTEM");
let mut msyscon = std::env::var_os("MSYSCON");
let mut msys2_path_type = std::env::var_os("MSYS2_PATH_TYPE");
let mut msys2_nostart = std::env::var_os("MSYS2_NOSTART");
let mut chere_invoking = std::env::var_os("CHERE_INVOKING");
// TODO: inherit from environment variable?
let mut shell_args = Vec::new();
let mut where_dir = None;
let mut loginshell = "bash".into();
let mut args = std::env::args_os();
args.next(); // skip args[0];
while let Some(arg) = args.next() {
// Help option
if arg == OsStr::new("-help")
|| arg == OsStr::new("--help")
|| arg == OsStr::new("-?")
|| arg == OsStr::new("/?")
{
printhelp();
return Err(ExitCode::SUCCESS);
} else if arg == OsStr::new("-msys") || arg == OsStr::new("-msys2") {
msystem = Some("MSYS".into());
} else if arg == OsStr::new("-mingw32") {
msystem = Some("MINGW32".into());
} else if arg == OsStr::new("-mingw64") {
msystem = Some("MINGW64".into());
} else if arg == OsStr::new("-ucrt64") {
msystem = Some("UCRT64".into());
} else if arg == OsStr::new("-clang64") {
msystem = Some("CLANG64".into());
} else if arg == OsStr::new("-clangarm64") {
msystem = Some("CLANGARM64".into());
} else if arg == OsStr::new("-mingw") {
if wd.join("../../mingw64").exists() {
msystem = Some("MINGW64".into());
} else {
msystem = Some("MINGW32".into());
}
}
// Console types
else if arg == OsStr::new("-mintty") {
msyscon = Some("mintty.exe".into());
} else if arg == OsStr::new("-conemu") {
msyscon = Some("conemu".into());
} else if arg == OsStr::new("-defterm") {
msyscon = Some("defterm".into());
}
// Other parameters
else if arg == OsStr::new("-full-path") || arg == OsStr::new("-use-full-path") {
msys2_path_type = Some("inherit".into());
} else if arg == OsStr::new("-here") {
chere_invoking = Some("enabled_from_arguments".into());
} else if arg == OsStr::new("-where") {
let Some(dir) = args.next() else {
eprintln!("Working directory is not specified for -where parameter.");
return Err(ExitCode::from(2));
};
let dir = PathBuf::from(dir);
if !dir.exists() {
eprintln!("Cannot set specified working directory \"{dir:?}\"");
return Err(ExitCode::from(2));
}
chere_invoking = Some("enabled_from_arguments".into());
where_dir = Some(dir);
} else if arg == OsStr::new("-no-start") {
msys2_nostart = Some("yes".into());
} else if arg == OsStr::new("-shell") {
let Some(shell) = args.next() else {
eprintln!("Shell not specified for -shell parameter.");
return Err(ExitCode::from(2));
};
loginshell = shell;
} else {
// Collect remaining command line arguments to be passed to shell
shell_args.push(arg);
shell_args.extend(args);
break;
}
}
Ok(Params {
msystem,
msyscon,
msys2_path_type,
msys2_nostart,
chere_invoking,
where_dir,
loginshell,
shell_args,
})
}
struct TitleAndIcon {
contitle: &'static str,
conicon: &'static str,
}
impl TitleAndIcon {
fn new(title: &'static str, icon: &'static str) -> Self {
TitleAndIcon {
contitle: title,
conicon: icon,
}
}
}
/// `:startmintty`
fn startmintty(wd: &Path, params: &Params, con: &TitleAndIcon) -> Command {
let mut command = Command::new(wd.join("mintty"));
command.arg("-i").arg(concat_os_str(["/", con.conicon]));
command.arg("-t").arg(con.contitle);
command.arg(concat_os_str([OsStr::new("/usr/bin/"), &params.loginshell]));
command.arg("-l");
command.args(params.shell_args.as_slice());
set_envs(&mut command, params, params.msyscon.as_deref());
return command;
fn concat_os_str<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(iter: I) -> OsString {
let mut result = OsString::new();
for s in iter.into_iter() {
result.push(s.as_ref());
}
result
}
}
/// `:startconemu`
fn startconemu(wd: &Path, params: &Params, con: &TitleAndIcon) -> Result<Command, ExitCode> {
let Some(conemu) = conemudetect() else {
eprintln!("ConEmu not found. Exiting.");
return Err(ExitCode::from(1));
};
let mut command = Command::new(conemu.command);
command.arg("/Here");
command
.arg("/Icon")
.arg(wd.join("..\\..\\").join(con.conicon));
command.arg("/cmd").arg(wd.join(&params.loginshell));
command.arg("-l");
command.args(params.shell_args.as_slice());
set_envs(&mut command, params, Some(&conemu.msyscon));
Ok(command)
}
/// `:startsh`
fn startsh(wd: &Path, params: &Params) -> Command {
let mut command = Command::new(wd.join(&params.loginshell));
command.arg("-l");
command.args(params.shell_args.as_slice());
set_envs(&mut command, params, None);
command
}
fn set_envs(command: &mut Command, params: &Params, msyscon: Option<&OsStr>) {
if let Some(where_dir) = params.where_dir.as_deref() {
command.current_dir(where_dir);
}
set_env(command, "MSYSTEM", params.msystem.as_ref());
set_env(command, "MSYSCON", msyscon);
set_env(command, "MSYS2_PATH_TYPE", params.msys2_path_type.as_ref());
set_env(command, "CHERE_INVOKING", params.chere_invoking.as_ref());
}
fn set_env(command: &mut Command, key: impl AsRef<OsStr>, value: Option<impl AsRef<OsStr>>) {
if let Some(value) = value {
let str = value.as_ref();
if !str.is_empty() {
command.env(key, value);
} else {
command.env_remove(key);
}
}
}
struct ConEmu {
command: PathBuf,
msyscon: OsString,
}
/// `:conemudetect`
fn conemudetect() -> Option<ConEmu> {
if let Some(con_emu_dir) = std::env::var_os("ConEmuDir") {
let con_emu64 = PathBuf::from(&con_emu_dir).join("ConEmu64.exe");
if con_emu64.exists() {
return Some(ConEmu {
command: con_emu64,
msyscon: "conemu64.exe".into(),
});
}
let con_emu = PathBuf::from(&con_emu_dir).join("ConEmu64.exe");
if con_emu.exists() {
return Some(ConEmu {
command: con_emu,
msyscon: "conemu.exe".into(),
});
}
}
if check("ConEmu64.exe") {
return Some(ConEmu {
command: PathBuf::from("ConEmu64.exe"),
msyscon: "conemu64.exe".into(),
});
} else if check("ConEmu.exe") {
return Some(ConEmu {
command: PathBuf::from("ConEmu.exe"),
msyscon: "conemu.exe".into(),
});
}
fn check(program: &str) -> bool {
Command::new(program)
.args(["/Exit"])
.stderr(Stdio::null())
.spawn()
.and_then(|mut child| child.wait())
.is_ok_and(|exit_status| exit_status.success())
}
// TODO: query registry
None
}
/// `:printhelp`
fn printhelp() {
let current_exe = std::env::current_exe().expect("current executable");
let name = current_exe.file_name().unwrap().to_string_lossy();
println!(
r#"
echo Usage:
echo {name} [options] [login shell parameters]
echo.
echo Options:
echo -mingw32 | -mingw64 | -ucrt64 | -clang64 |
echo -msys[2] | -clangarm64 Set shell type
echo -defterm | -mintty | -conemu Set terminal type
echo -here Use current directory as working
echo directory
echo -where DIRECTORY Use specified DIRECTORY as working
echo directory
echo -[use-]full-path Use full current PATH variable
echo instead of trimming to minimal
echo -no-start Do not use "start" command and
echo return login shell resulting
echo errorcode as this batch file
echo resulting errorcode
echo -shell SHELL Set login shell
echo -help | --help | -? | /? Display this help and exit
echo.
echo Any parameter that cannot be treated as valid option and all
echo following parameters are passed as login shell command parameters.
echo."#
)
}
@foriequal0
Copy link
Copy Markdown
Author

foriequal0 commented May 11, 2026

I'm annoyed by the following prompt when I terminate the terminal with Ctrl+D in Windows Terminal.

Terminate batch job (Y/N)? y

So I ported msys2_shell.cmd into a native binary with Rust.

@foriequal0
Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment