use std::collections::HashMap;
use std::sync::atomic::AtomicI32;
use crate::ported::utils::zwarnnam;
use crate::ported::zsh_h::EMULATE_FULLY;
use crate::ported::zsh_h::{APPENDHISTORY, BANGHIST, CHASELINKS, GLOBDOTS, HASHCMDS, HISTNOFUNCTIONS, IGNOREBRACES, MAILWARNING, PROMPTSUBST, SHINSTDIN, SINGLECOMMAND, };
use crate::ported::zsh_h::OPT_SIZE;
use crate::ported::zsh_h as zh;
#[allow(non_upper_case_globals)]
pub static emulation: AtomicI32 = AtomicI32::new(0);
const OPT_CSH: u8 = crate::ported::zsh_h::EMULATE_CSH as u8; const OPT_KSH: u8 = crate::ported::zsh_h::EMULATE_KSH as u8; const OPT_SH: u8 = crate::ported::zsh_h::EMULATE_SH as u8; const OPT_ZSH: u8 = crate::ported::zsh_h::EMULATE_ZSH as u8; const OPT_ALL: u8 = OPT_CSH | OPT_KSH | OPT_SH | OPT_ZSH; const OPT_BOURNE: u8 = OPT_KSH | OPT_SH; const OPT_BSHELL: u8 = OPT_KSH | OPT_SH | OPT_ZSH; const OPT_NONBOURNE: u8 = OPT_ALL & !OPT_BOURNE; const OPT_NONZSH: u8 = OPT_ALL & !OPT_ZSH;
const OPT_EMULATE: u16 = crate::ported::zsh_h::EMULATE_UNUSED as u16; const OPT_SPECIAL: u16 = (crate::ported::zsh_h::EMULATE_UNUSED << 1) as u16; const OPT_ALIAS: u16 = (crate::ported::zsh_h::EMULATE_UNUSED << 2) as u16;
pub static zshletters: &[(char, &str, bool)] = &[
('0', "correct", false),
('1', "printexitvalue", false),
('2', "badpattern", true),
('3', "nomatch", true),
('4', "globdots", false),
('5', "notify", false),
('6', "bgnice", false),
('7', "ignoreeof", false),
('8', "markdirs", false),
('9', "autolist", false),
('B', "beep", true),
('C', "clobber", true),
('D', "pushdtohome", false),
('E', "pushdsilent", false),
('F', "glob", true),
('G', "nullglob", false),
('H', "rmstarsilent", false),
('I', "ignorebraces", false),
('J', "autocd", false),
('K', "banghist", true),
('L', "sunkeyboardhack", false),
('M', "singlelinezle", false),
('N', "autopushd", false),
('O', "correctall", false),
('P', "rcexpandparam", false),
('Q', "pathdirs", false),
('R', "longlistjobs", false),
('S', "recexact", false),
('T', "cdablevars", false),
('U', "mailwarning", false),
('V', "promptcr", true),
('W', "autoresume", false),
('X', "listtypes", false),
('Y', "menucomplete", false),
('Z', "zle", false),
('a', "allexport", false),
('d', "globalrcs", true),
('e', "errexit", false),
('f', "rcs", true),
('g', "histignorespace", false),
('h', "histignoredups", false),
('i', "interactive", false),
('k', "interactivecomments", false),
('l', "login", false),
('m', "monitor", false),
('n', "exec", true),
('p', "privileged", false),
('s', "shinstdin", false),
('t', "singlecommand", false),
('u', "unset", true),
('v', "verbose", false),
('w', "chaselinks", false),
('x', "xtrace", false),
('y', "shwordsplit", false),
];
pub static KSH_LETTERS: &[(char, &str, bool)] = &[
('C', "clobber", true),
('T', "trapsasync", false),
('X', "markdirs", false),
('a', "allexport", false),
('b', "notify", false),
('e', "errexit", false),
('f', "glob", true),
('i', "interactive", false),
('l', "login", false),
('m', "monitor", false),
('n', "exec", true),
('p', "privileged", false),
('s', "shinstdin", false),
('t', "singlecommand", false),
('u', "unset", true),
('v', "verbose", false),
('x', "xtrace", false),
];
pub static EMULATION: std::sync::atomic::AtomicI32 =
std::sync::atomic::AtomicI32::new(crate::ported::zsh_h::EMULATE_ZSH);
pub static FULLY_EMULATING: std::sync::atomic::AtomicBool =
std::sync::atomic::AtomicBool::new(false);
pub fn emulate(mode: &str, fully: bool) { let ch = mode.chars().next().unwrap_or('z');
let ch = if ch == 'r' { mode.chars().nth(1).unwrap_or('z') } else { ch };
let new_emu = match ch {
'c' => crate::ported::zsh_h::EMULATE_CSH,
'k' => crate::ported::zsh_h::EMULATE_KSH,
's' | 'b' => crate::ported::zsh_h::EMULATE_SH,
_ => crate::ported::zsh_h::EMULATE_ZSH,
};
EMULATION.store(new_emu, std::sync::atomic::Ordering::Relaxed);
FULLY_EMULATING.store(fully, std::sync::atomic::Ordering::Relaxed);
let mut emu = new_emu;
if fully {
emu |= crate::ported::zsh_h::EMULATE_FULLY; }
let mut new_opts: std::collections::HashMap<String, bool> =
std::collections::HashMap::new();
installemulation(emu, &mut new_opts); for (k, v) in &new_opts {
if (optns_flags(k) & OPT_SPECIAL) == 0 { opt_state_set(k, *v);
}
}
if new_emu == crate::ported::zsh_h::EMULATE_ZSH {
for name in ZSH_OPTIONS_SET.iter() {
opt_state_set(name, defset(name, EMULATE_ZSH));
}
}
}
#[cfg(test)]
mod tests {
use crate::zsh_h::isset;
use super::*;
static TEST_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
#[test]
fn test_default_options() {
let _g = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
createoptiontable();
assert!(isset(optlookup("glob")));
assert!(!isset(optlookup("xtrace")));
assert!(!isset(optlookup("zle")));
}
#[test]
fn test_set_option() {
let _g = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
createoptiontable();
dosetopt(optlookup("xtrace"), if true { 1 } else { 0 }, 0);
assert!(isset(optlookup("xtrace")));
dosetopt(optlookup("xtrace"), if false { 1 } else { 0 }, 0);
assert!(!isset(optlookup("xtrace")));
}
#[test]
fn test_no_prefix() {
let _g = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
createoptiontable();
dosetopt(optlookup("noglob"), if true { 1 } else { 0 }, 0);
assert!(!isset(optlookup("glob")));
let n = optlookup("noglob");
assert!(n < 0, "noglob should resolve to negative optno");
assert!(!isset(-n), "after `setopt noglob`, glob must be unset");
}
#[test]
fn test_case_insensitive() {
let _g = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
createoptiontable();
assert_eq!(optlookup("GLOB"), optlookup("glob"));
assert_eq!(optlookup("GlOb"), optlookup("glob"));
}
#[test]
fn test_underscore_ignored() {
let _g = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
createoptiontable();
assert_eq!(optlookup("auto_list"), optlookup("autolist"));
assert_eq!(optlookup("AUTO_LIST"), optlookup("autolist"));
}
#[test]
fn test_option_alias() {
let _g = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
createoptiontable();
dosetopt(optlookup("braceexpand"), if true { 1 } else { 0 }, 0);
assert!(!isset(optlookup("ignorebraces")));
}
#[test]
fn test_single_letter() {
let _g = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
createoptiontable();
dosetopt(optlookupc('x'), if true { 1 } else { 0 }, 0);
assert!(isset(optlookup("xtrace")));
dosetopt(optlookupc('n'), if true { 1 } else { 0 }, 0);
assert!(!isset(optlookup("exec")));
}
#[test]
fn test_emulation() {
let _g = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
emulate("sh", true);
assert_eq!(
EMULATION.load(std::sync::atomic::Ordering::Relaxed),
crate::ported::zsh_h::EMULATE_SH
);
assert!(isset(optlookup("shwordsplit")));
emulate("zsh", true);
assert_eq!(
EMULATION.load(std::sync::atomic::Ordering::Relaxed),
crate::ported::zsh_h::EMULATE_ZSH
);
}
#[test]
fn test_dash_string() {
let _g = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
opt_state_set("interactive", true);
opt_state_set("monitor", true);
let dash = dashgetfn();
assert!(dash.contains('i'));
assert!(dash.contains('m'));
}
#[test]
fn test_lookup_canonicalises_underscores_and_case() {
let _g = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
createoptiontable();
assert_eq!(optlookup("AUTO_LIST"), optlookup("autolist"));
assert_eq!(optlookup("AutoList"), optlookup("autolist"));
assert_eq!(optlookup("auto__list"), optlookup("autolist"));
}
}
use std::collections::HashSet;
use std::sync::LazyLock;
use crate::zsh_h::{isset, EMULATE_ZSH};
pub(crate) static ZSH_OPTIONS_SET: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
[
"aliases",
"allexport",
"alwayslastprompt",
"alwaystoend",
"appendcreate",
"appendhistory",
"autocd",
"autocontinue",
"autolist",
"automenu",
"autonamedirs",
"autoparamkeys",
"autoparamslash",
"autopushd",
"autoremoveslash",
"autoresume",
"badpattern",
"banghist",
"bareglobqual",
"bashautolist",
"bashrematch",
"beep",
"bgnice",
"braceccl",
"bsdecho",
"caseglob",
"casematch",
"cbases",
"cdablevars",
"cdsilent",
"chasedots",
"chaselinks",
"checkjobs",
"checkrunningjobs",
"clobber",
"combiningchars",
"completealiases",
"completeinword",
"continueonerror",
"correct",
"correctall",
"cprecedences",
"cshjunkiehistory",
"cshjunkieloops",
"cshjunkiequotes",
"cshnullcmd",
"cshnullglob",
"debugbeforecmd",
"dotglob",
"dvorak",
"emacs",
"equals",
"errexit",
"errreturn",
"evallineno",
"exec",
"extendedglob",
"extendedhistory",
"flowcontrol",
"forcefloat",
"functionargzero",
"glob",
"globassign",
"globcomplete",
"globdots",
"globstarshort",
"globsubst",
"globalexport",
"globalrcs",
"hashall",
"hashcmds",
"hashdirs",
"hashexecutablesonly",
"hashlistall",
"histallowclobber",
"histappend",
"histbeep",
"histexpand",
"histexpiredupsfirst",
"histfcntllock",
"histfindnodups",
"histignorealldups",
"histignoredups",
"histignorespace",
"histlexwords",
"histnofunctions",
"histnostore",
"histreduceblanks",
"histsavebycopy",
"histsavenodups",
"histsubstpattern",
"histverify",
"hup",
"ignorebraces",
"ignoreclosebraces",
"ignoreeof",
"incappendhistory",
"incappendhistorytime",
"interactive",
"interactivecomments",
"ksharrays",
"kshautoload",
"kshglob",
"kshoptionprint",
"kshtypeset",
"kshzerosubscript",
"listambiguous",
"listbeep",
"listpacked",
"listrowsfirst",
"listtypes",
"localloops",
"localoptions",
"localpatterns",
"localtraps",
"log",
"login",
"longlistjobs",
"magicequalsubst",
"mailwarn",
"mailwarning",
"markdirs",
"menucomplete",
"monitor",
"multibyte",
"multifuncdef",
"multios",
"nomatch",
"notify",
"nullglob",
"numericglobsort",
"octalzeroes",
"onecmd",
"overstrike",
"pathdirs",
"pathscript",
"physical",
"pipefail",
"posixaliases",
"posixargzero",
"posixbuiltins",
"posixcd",
"posixidentifiers",
"posixjobs",
"posixstrings",
"posixtraps",
"printeightbit",
"printexitvalue",
"privileged",
"promptbang",
"promptcr",
"promptpercent",
"promptsp",
"promptsubst",
"promptvars",
"pushdignoredups",
"pushdminus",
"pushdsilent",
"pushdtohome",
"rcexpandparam",
"rcquotes",
"rcs",
"recexact",
"rematchpcre",
"restricted",
"rmstarsilent",
"rmstarwait",
"sharehistory",
"shfileexpansion",
"shglob",
"shinstdin",
"shnullcmd",
"shoptionletters",
"shortloops",
"shortrepeat",
"shwordsplit",
"singlecommand",
"singlelinezle",
"sourcetrace",
"stdin",
"sunkeyboardhack",
"trackall",
"transientrprompt",
"trapsasync",
"typesetsilent",
"typesettounset",
"unset",
"verbose",
"vi",
"warncreateglobal",
"warnnestedvar",
"xtrace",
"zle",
"braceexpand", "dotglob", "hashall", "histappend", "histexpand", "log", "mailwarn", "onecmd", "physical", "promptvars", ]
.into_iter()
.collect()
});
pub const OPT_INVALID: i32 = -10000;
pub fn createoptiontable() { let zsh_emu = EMULATE_ZSH;
for name in ZSH_OPTIONS_SET.iter() { opt_state_set(name, defset(name, zsh_emu));
}
}
pub fn printoptionnode(hn: &str, set: bool) { let on = opt_state_get(hn).unwrap_or(false); let default_on = default_on_options().contains(&hn); let kshprint = opt_state_get("kshoptionprint").unwrap_or(false); if kshprint { if default_on { println!("no{:<19} {}", hn, if on { "off" } else { "on" }); } else {
println!("{:<21} {}", hn, if on { "on" } else { "off" }); }
} else if set == (on ^ default_on) { if set ^ on { print!("no"); }
println!("{}", hn); }
}
#[inline]
pub fn defset(X: &str, my_emulation: i32) -> bool {
let flags = optns_flags(X);
(flags & (my_emulation as u16)) != 0
}
fn optns_flags(name: &str) -> u16 {
match name.to_lowercase().as_str() {
"aliases" => OPT_EMULATE | (OPT_ALL as u16), "aliasfuncdef" => OPT_EMULATE | (OPT_BOURNE as u16), "allexport" => OPT_EMULATE, "alwayslastprompt" => OPT_ALL as u16, "alwaystoend" => 0, "appendcreate" => OPT_EMULATE | (OPT_BOURNE as u16), "appendhistory" => OPT_ALL as u16, "autocd" => OPT_EMULATE, "autocontinue" => 0, "autolist" => OPT_ALL as u16, "automenu" => OPT_ALL as u16, "autonamedirs" => 0, "autoparamkeys" => OPT_ALL as u16, "autoparamslash" => OPT_ALL as u16, "autopushd" => 0, "autoremoveslash" => OPT_ALL as u16, "autoresume" => 0, "badpattern" => OPT_EMULATE | (OPT_NONBOURNE as u16), "banghist" => OPT_NONBOURNE as u16, "bareglobqual" => OPT_EMULATE | (OPT_ZSH as u16), "bashautolist" => 0, "bashrematch" => OPT_EMULATE, "beep" => OPT_ALL as u16, "bgnice" => OPT_EMULATE | (OPT_NONBOURNE as u16), "braceccl" => 0, "bsdecho" => OPT_EMULATE, "caseglob" => OPT_ALL as u16, "casematch" => OPT_ALL as u16, "cbases" => 0, "cdablevars" => OPT_EMULATE, "cdsilent" => 0, "chasedots" => 0, "chaselinks" => 0, "checkjobs" => OPT_EMULATE | (OPT_ZSH as u16), "checkrunningjobs" => OPT_EMULATE | (OPT_ZSH as u16), "clobber" => OPT_EMULATE | (OPT_ALL as u16), "combiningchars" => 0, "completealiases" => 0, "completeinword" => 0, "correct" => 0, "correctall" => 0, "cprecedences" => OPT_EMULATE, "cshjunkiehistory" => OPT_EMULATE, "cshjunkieloops" => OPT_EMULATE, "cshjunkiequotes" => OPT_EMULATE, "cshnullcmd" => OPT_EMULATE, "cshnullglob" => OPT_EMULATE, "debugbeforecmd" => OPT_ALL as u16, "emacs" => 0, "equals" => OPT_EMULATE | (OPT_NONBOURNE as u16), "errexit" => OPT_EMULATE, "errreturn" => OPT_EMULATE, "exec" => OPT_ALL as u16, "extendedglob" => OPT_EMULATE, "extendedhistory" => OPT_CSH as u16, "evallineno" => OPT_EMULATE | (OPT_ZSH as u16), "flowcontrol" => OPT_ALL as u16, "forcefloat" => 0, "functionargzero" => OPT_EMULATE | (OPT_NONBOURNE as u16), "glob" => OPT_EMULATE | (OPT_ALL as u16), "globalexport" => OPT_EMULATE | (OPT_ZSH as u16), "globalrcs" => OPT_ALL as u16, "globassign" => OPT_EMULATE, "globcomplete" => 0, "globdots" => OPT_EMULATE, "globstarshort" => OPT_EMULATE, "globsubst" => OPT_EMULATE | (OPT_NONZSH as u16), "hashcmds" => OPT_ALL as u16, "hashdirs" => OPT_ALL as u16, "hashexecutablesonly" => 0, "hashlistall" => OPT_ALL as u16, "histallowclobber" => 0, "histbeep" => OPT_ALL as u16, "histexpiredupsfirst" => 0, "histfcntllock" => 0, "histfindnodups" => 0, "histignorealldups" => 0, "histignoredups" => 0, "histignorespace" => 0, "histlexwords" => 0, "histnofunctions" => 0, "histnostore" => 0, "histreduceblanks" => 0, "histsavebycopy" => OPT_ALL as u16, "histsavenodups" => 0, "histsubstpattern" => OPT_EMULATE, "histverify" => 0, "hup" => OPT_EMULATE | (OPT_ZSH as u16), "ignorebraces" => OPT_EMULATE | (OPT_SH as u16), "ignoreclosebraces" => 0, "ignoreeof" => 0, "incappendhistory" => 0, "incappendhistorytime" => 0, "interactive" => OPT_SPECIAL as u16, "interactivecomments" => OPT_EMULATE | (OPT_BOURNE as u16), "ksharrays" => OPT_EMULATE | (OPT_BOURNE as u16), "kshautoload" => OPT_EMULATE | (OPT_BOURNE as u16), "kshglob" => OPT_EMULATE | (OPT_KSH as u16), "kshoptionprint" => OPT_EMULATE | (OPT_KSH as u16), "kshtypeset" => OPT_EMULATE | (OPT_BOURNE as u16), "kshzerosubscript" => OPT_EMULATE | (OPT_BOURNE as u16), "listambiguous" => OPT_ALL as u16, "listbeep" => OPT_ALL as u16, "listpacked" => 0, "listrowsfirst" => 0, "listtypes" => OPT_ALL as u16, "localoptions" => OPT_EMULATE | (OPT_KSH as u16), "localloops" => 0, "localpatterns" => 0, "localtraps" => OPT_EMULATE | (OPT_KSH as u16), "loginshell" => OPT_SPECIAL as u16, "longlistjobs" => 0, "magicequalsubst" => OPT_EMULATE, "mailwarning" => 0, "markdirs" => 0, "menucomplete" => 0, "monitor" => OPT_SPECIAL as u16, "multibyte" => 0, "multifuncdef" => OPT_EMULATE | (OPT_ZSH as u16), "multios" => OPT_EMULATE | (OPT_ZSH as u16), "nomatch" => OPT_EMULATE | (OPT_NONBOURNE as u16), "notify" => OPT_EMULATE | (OPT_ZSH as u16), "nullglob" => OPT_EMULATE, "numericglobsort" => 0, "octalzeroes" => OPT_EMULATE | (OPT_SH as u16), "overstrike" => 0, "pathdirs" => 0, "pathscript" => OPT_EMULATE | (OPT_BOURNE as u16), "pipefail" => OPT_EMULATE, "posixaliases" => OPT_EMULATE | (OPT_BOURNE as u16), "posixargzero" => OPT_EMULATE | (OPT_BOURNE as u16), "posixbuiltins" => OPT_EMULATE | (OPT_BOURNE as u16), "posixcd" => OPT_EMULATE | (OPT_BOURNE as u16), "posixidentifiers" => OPT_EMULATE | (OPT_BOURNE as u16), "posixjobs" => OPT_EMULATE | (OPT_BOURNE as u16), "posixstrings" => OPT_EMULATE | (OPT_BOURNE as u16), "posixtraps" => OPT_EMULATE | (OPT_BOURNE as u16), "printeightbit" => 0, "printexitvalue" => 0, "privileged" => OPT_SPECIAL as u16, "promptbang" => OPT_EMULATE | (OPT_KSH as u16), "promptcr" => OPT_ALL as u16, "promptpercent" => OPT_EMULATE | (OPT_NONBOURNE as u16), "promptsp" => OPT_ALL as u16, "promptsubst" => OPT_EMULATE | (OPT_BOURNE as u16), "pushdignoredups" => 0, "pushdminus" => 0, "pushdsilent" => 0, "pushdtohome" => 0, "rcexpandparam" => OPT_EMULATE, "rcquotes" => 0, "rcs" => OPT_ALL as u16, "recexact" => 0, "rematchpcre" => 0, "restricted" => OPT_SPECIAL as u16, "rmstarsilent" => OPT_EMULATE | (OPT_BOURNE as u16), "rmstarwait" => 0, "sharehistory" => 0, "shfileexpansion" => OPT_EMULATE | (OPT_BOURNE as u16), "shglob" => OPT_EMULATE | (OPT_BOURNE as u16), "shinstdin" => OPT_SPECIAL as u16, "shnullcmd" => OPT_EMULATE | (OPT_BOURNE as u16), "shoptionletters" => OPT_EMULATE | (OPT_BOURNE as u16), "shortloops" => OPT_EMULATE | (OPT_NONBOURNE as u16), "shortrepeat" => OPT_EMULATE | (OPT_ZSH as u16), "shwordsplit" => OPT_EMULATE | (OPT_BOURNE as u16), "singlecommand" => OPT_SPECIAL as u16, "singlelinezle" => 0, "sourcetrace" => 0, "sunkeyboardhack" => 0, "transientrprompt" => 0, "trapsasync" => 0, "typesetsilent" => OPT_EMULATE | (OPT_BOURNE as u16), "unset" => OPT_EMULATE | (OPT_BSHELL as u16), "verbose" => OPT_EMULATE, "vi" => 0, "warncreateglobal" => 0, "warnnestedvar" => 0, "xtrace" => OPT_EMULATE, "zle" => OPT_SPECIAL as u16, "dvorak" => 0, _ => 0,
}
}
pub(crate) fn default_on_options() -> std::collections::HashSet<&'static str> {
let zsh_emu = crate::ported::zsh_h::EMULATE_ZSH as u16;
let mut set = std::collections::HashSet::new();
for name in ZSH_OPTIONS_SET.iter() {
let flags = optns_flags(name);
if (flags & zsh_emu) != 0 && (flags & OPT_SPECIAL) == 0 {
set.insert(*name);
}
}
set
}
static SETEMULATE_EMULATION: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
static SETEMULATE_OPTS: std::sync::OnceLock<
std::sync::Mutex<std::collections::HashMap<String, bool>>,
> = std::sync::OnceLock::new();
fn setemulate_opts_lock()
-> &'static std::sync::Mutex<std::collections::HashMap<String, bool>>
{
SETEMULATE_OPTS
.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()))
}
pub fn setemulate(name: &str, fully: i32) { let flags = optns_flags(name); let is_alias = (flags & OPT_ALIAS) != 0;
let is_special = (flags & OPT_SPECIAL) != 0;
let is_emulate = (flags & OPT_EMULATE) != 0;
if is_alias {
return;
}
if !((fully != 0 && !is_special) || is_emulate) { return;
}
let target = SETEMULATE_EMULATION.load(std::sync::atomic::Ordering::Relaxed);
let on_by_default = defset(name, target);
if let Ok(mut tab) = setemulate_opts_lock().lock() {
tab.insert(name.to_string(), on_by_default);
}
}
pub fn installemulation(new_emulation: i32,
new_opts: &mut std::collections::HashMap<String, bool>) { SETEMULATE_EMULATION
.store(new_emulation, std::sync::atomic::Ordering::Relaxed); if let Ok(mut tab) = setemulate_opts_lock().lock() {
tab.clear();
}
let fully = if (new_emulation & EMULATE_FULLY) != 0 { 1 } else { 0 }; for name in ZSH_OPTIONS_SET.iter() {
setemulate(name, fully); }
if let Ok(tab) = setemulate_opts_lock().lock() {
for (k, v) in tab.iter() {
new_opts.insert(k.clone(), *v);
}
}
}
pub fn setoption(hn: &str, value: i32) -> i32 {
opt_state_set(hn, value != 0); 0
}
pub fn optlookup(name: &str) -> i32 {
let s: String = name.chars() .filter(|&c| c != '_') .flat_map(|c| c.to_lowercase()) .collect();
let alias_optno: Option<i32> = match s.as_str() {
"braceexpand" => Some(-IGNOREBRACES), "dotglob" => Some(GLOBDOTS), "hashall" => Some(HASHCMDS), "histappend" => Some(APPENDHISTORY), "histexpand" => Some(BANGHIST), "log" => Some(-HISTNOFUNCTIONS), "mailwarn" => Some(MAILWARNING), "onecmd" => Some(SINGLECOMMAND), "physical" => Some(CHASELINKS), "promptvars" => Some(PROMPTSUBST), "stdin" => Some(SHINSTDIN), "trackall" => Some(HASHCMDS), _ => None,
};
if let Some(optno) = alias_optno {
return optno;
}
if let Some(stripped) = s.strip_prefix("no") { if let Some(optno) = optno_by_name(stripped) { return -optno; }
}
match optno_by_name(&s) { Some(optno) => optno, None => OPT_INVALID, }
}
pub fn optlookupc(c: char) -> i32 { let letters = if crate::ported::zsh_h::isset(optlookup("shoptionletters")) {
KSH_LETTERS
} else {
zshletters
};
for (ch, name, _negated) in letters {
if *ch == c {
return optno_by_name(name).unwrap_or(0); }
}
0
}
fn optno_by_name(name: &str) -> Option<i32> {
for idx in 1..OPT_SIZE {
if index_to_name(idx) == Some(name) {
return Some(idx);
}
}
None
}
static OPTS_LIVE: std::sync::OnceLock<
std::sync::RwLock<std::collections::HashMap<String, bool>>> =
std::sync::OnceLock::new();
pub fn opt_state_get(name: &str) -> Option<bool> {
let m = OPTS_LIVE.get_or_init(|| std::sync::RwLock::new(
std::collections::HashMap::new()));
m.read().ok().and_then(|g| g.get(name).copied())
}
pub fn opt_state_set(name: &str, value: bool) {
let m = OPTS_LIVE.get_or_init(|| std::sync::RwLock::new(
std::collections::HashMap::new()));
if let Ok(mut g) = m.write() {
g.insert(name.to_string(), value);
}
}
pub fn opt_state_unset(name: &str) {
let m = OPTS_LIVE.get_or_init(|| std::sync::RwLock::new(
std::collections::HashMap::new()));
if let Ok(mut g) = m.write() {
g.remove(name);
}
}
pub fn opt_state_snapshot() -> std::collections::HashMap<String, bool> {
let m = OPTS_LIVE.get_or_init(|| std::sync::RwLock::new(
std::collections::HashMap::new()));
m.read().map(|g| g.clone()).unwrap_or_default()
}
pub fn opt_state_len() -> usize {
let m = OPTS_LIVE.get_or_init(|| std::sync::RwLock::new(
std::collections::HashMap::new()));
m.read().map(|g| g.len()).unwrap_or(0)
}
pub fn dosetopt(optno: i32, mut value: i32, _force: i32) -> i32 { if optno == 0 { return -1; }
let mut idx = optno;
if idx < 0 { idx = -idx;
value = if value != 0 { 0 } else { 1 }; }
let name = ZSH_OPTIONS_SET.iter().find(|n| optlookup(n) == idx);
match name {
Some(n) => { opt_state_set(n, value != 0); 0 } None => -1, }
}
pub fn bin_setopt(nam: &str, args: &[String], _ops: &crate::ported::zsh_h::options, isun: i32) -> i32 {
let mut retval = 0i32;
let mut match_glob = false; let mut idx = 0usize;
if args.is_empty() { let want_set = isun == 0;
let mut names: Vec<String> = ZSH_OPTIONS_SET.iter()
.map(|s| s.to_string()).collect();
names.sort();
for n in names {
let on = opt_state_get(&n).unwrap_or(false);
if on == want_set {
printoptionnode(&n, want_set); }
}
return 0; }
'outer: while idx < args.len()
&& (args[idx].starts_with('-') || args[idx].starts_with('+'))
{
let leading = args[idx].as_bytes()[0]; let action: i32 = ((leading == b'-') as i32) ^ isun; if args[idx].len() == 1 { idx += 1;
break 'outer;
}
let body_bytes = args[idx].as_bytes()[1..].to_vec(); let mut k = 0usize;
while k < body_bytes.len() { let mut c = body_bytes[k];
if c == crate::ported::zsh_h::META as u8 { k += 1;
if k < body_bytes.len() { c = body_bytes[k] ^ 32; } else { break; }
}
if c == b'-' { idx += 1; break 'outer; } else if c == b'o' { let oarg: String = if k + 1 < body_bytes.len() { String::from_utf8_lossy(&body_bytes[k + 1..]).into_owned()
} else {
idx += 1; if idx >= args.len() { zwarnnam(nam, "string expected after -o"); return 1; }
args[idx].clone()
};
let optno = optlookup(&oarg); if optno == 0 { zwarnnam(nam, &format!("no such option: {}", oarg));
retval |= 1;
} else if dosetopt(optno, action, 0) != 0 { zwarnnam(nam, &format!("can't change option: {}", oarg));
retval |= 1;
}
break; } else if c == b'm' { match_glob = true; } else { let optno = optlookupc(c as char); if optno == 0 { zwarnnam(nam, &format!("bad option: -{}", c as char)); retval |= 1;
} else if dosetopt(optno, action, 0) != 0 { zwarnnam(nam, &format!("can't change option: -{}", c as char));
retval |= 1;
}
}
k += 1;
}
idx += 1; }
if !match_glob { while idx < args.len() { let oname = args[idx].clone();
idx += 1;
let optno = optlookup(&oname); if optno == 0 { zwarnnam(nam, &format!("no such option: {}", oname));
retval |= 1;
} else {
let v = (isun == 0) as i32; if dosetopt(optno, v, 0) != 0 { zwarnnam(nam, &format!("can't change option: {}", oname));
retval |= 1;
}
}
}
} else { while idx < args.len() { let raw = args[idx].clone();
idx += 1;
let normalized: String = raw.chars()
.filter(|&c| c != '_')
.map(|c| c.to_ascii_lowercase()).collect();
let prog = crate::ported::pattern::patcompile(
&normalized,
crate::ported::zsh_h::PAT_HEAPDUP,
None,
);
if prog.is_none() { zwarnnam(nam, &format!("bad pattern: {}", raw)); retval |= 1;
break; }
let v = (isun == 0) as i32;
for opt_name in ZSH_OPTIONS_SET.iter() { if crate::ported::pattern::patmatch(&normalized, opt_name) {
let _ = setoption(opt_name, v); }
}
}
}
crate::ported::utils::inittyptab(); retval }
pub fn dashgetfn() -> String {
let letters = if crate::ported::zsh_h::isset(optlookup("shoptionletters")) {
KSH_LETTERS
} else {
zshletters
};
let mut out = String::new();
for c in (b'A'..=b'z').map(|b| b as char) {
for (ch, name, negated) in letters {
if *ch == c {
let value = opt_state_get(name).unwrap_or(false); let effective = if *negated { !value } else { value };
if effective {
out.push(c);
}
break;
}
}
}
out
}
pub fn printoptionstates(hadplus: bool) { let mut names: Vec<&'static str> = ZSH_OPTIONS_SET.iter().copied().collect();
names.sort();
for n in names { let value = opt_state_get(n).unwrap_or(false);
printoptionnodestate(n, value, hadplus); }
}
pub fn printoptionnodestate(name: &str, value: bool, hadplus: bool) { let default_on = default_on_options().contains(&name); if hadplus { let sign = if default_on != value { '-' } else { '+' }; let no_prefix = if default_on { "no" } else { "" }; println!("set {}o {}{}", sign, no_prefix, name); } else {
if default_on { println!("no{:<19} {}", name, if value { "off" } else { "on" });
} else {
println!("{:<21} {}", name, if value { "on" } else { "off" });
}
}
}
pub fn printoptionlist() { println!();
println!("Named options:"); let mut names: Vec<&'static str> = ZSH_OPTIONS_SET.iter().copied().collect();
names.sort();
for n in &names { printoptionlist_printoption(n, 0); }
println!();
println!("Option aliases:"); println!();
println!("Option letters:"); let letters = if crate::ported::zsh_h::isset(optlookup("shoptionletters")) {
KSH_LETTERS
} else {
zshletters
};
for c in (b'A'..=b'z').map(|b| b as char) { for (ch, aname, _negated) in letters {
if *ch == c {
print!(" -{} ", c); printoptionlist_printequiv(optlookup(aname)); break;
}
}
}
}
pub fn printoptionlist_printoption(name: &str, _ignored: i32) { println!(" --{}", name); }
pub fn printoptionlist_printequiv(optno: i32) { let isneg = optno < 0; let abs_optno = if isneg { -optno } else { optno }; let prefix = if isneg { "no-" } else { "" }; let name = ZSH_OPTIONS_SET.iter()
.find(|n| optlookup(n) == abs_optno)
.copied()
.unwrap_or("?"); println!(" equivalent to --{}{}", prefix, name); }
pub fn print_emulate_option(name: &str, value: bool, _fully: bool) { if !value { print!("no"); }
println!("{}", name); }
pub fn list_emulate_options(cmdopts: &std::collections::HashMap<String, bool>,
fully: bool) { let mut names: Vec<&'static str> = ZSH_OPTIONS_SET.iter().copied().collect();
names.sort();
for n in names { let value = cmdopts.get(n).copied().unwrap_or(false);
print_emulate_option(n, value, fully); }
}
fn index_to_name(idx: i32) -> Option<&'static str> {
let i = idx.unsigned_abs() as i32;
Some(match i {
x if x == zh::ALIASESOPT => "aliases",
x if x == zh::ALIASFUNCDEF => "aliasfuncdef",
x if x == zh::ALLEXPORT => "allexport",
x if x == zh::ALWAYSLASTPROMPT => "alwayslastprompt",
x if x == zh::ALWAYSTOEND => "alwaystoend",
x if x == zh::APPENDCREATE => "appendcreate",
x if x == zh::APPENDHISTORY => "appendhistory",
x if x == zh::AUTOCD => "autocd",
x if x == zh::AUTOCONTINUE => "autocontinue",
x if x == zh::AUTOLIST => "autolist",
x if x == zh::AUTOMENU => "automenu",
x if x == zh::AUTONAMEDIRS => "autonamedirs",
x if x == zh::AUTOPARAMKEYS => "autoparamkeys",
x if x == zh::AUTOPARAMSLASH => "autoparamslash",
x if x == zh::AUTOPUSHD => "autopushd",
x if x == zh::AUTOREMOVESLASH => "autoremoveslash",
x if x == zh::AUTORESUME => "autoresume",
x if x == zh::BADPATTERN => "badpattern",
x if x == zh::BANGHIST => "banghist",
x if x == zh::BAREGLOBQUAL => "bareglobqual",
x if x == zh::BASHAUTOLIST => "bashautolist",
x if x == zh::BASHREMATCH => "bashrematch",
x if x == zh::BEEP => "beep",
x if x == zh::BGNICE => "bgnice",
x if x == zh::BRACECCL => "braceccl",
x if x == zh::BSDECHO => "bsdecho",
x if x == zh::CASEGLOB => "caseglob",
x if x == zh::CASEMATCH => "casematch",
x if x == zh::CDABLEVARS => "cdablevars",
x if x == zh::CHASEDOTS => "chasedots",
x if x == zh::CHASELINKS => "chaselinks",
x if x == zh::CHECKJOBS => "checkjobs",
x if x == zh::CLOBBER => "clobber",
x if x == zh::COMBININGCHARS => "combiningchars",
x if x == zh::COMPLETEALIASES => "completealiases",
x if x == zh::COMPLETEINWORD => "completeinword",
x if x == zh::CORRECT => "correct",
x if x == zh::CORRECTALL => "correctall",
x if x == zh::CPRECEDENCES => "cprecedences",
x if x == zh::CSHJUNKIEHISTORY => "cshjunkiehistory",
x if x == zh::CSHJUNKIELOOPS => "cshjunkieloops",
x if x == zh::CSHJUNKIEQUOTES => "cshjunkiequotes",
x if x == zh::CSHNULLCMD => "cshnullcmd",
x if x == zh::CSHNULLGLOB => "cshnullglob",
x if x == zh::DEBUGBEFORECMD => "debugbeforecmd",
x if x == zh::EMACSMODE => "emacsmode",
x if x == zh::EQUALSOPT => "equals",
x if x == zh::ERREXIT => "errexit",
x if x == zh::ERRRETURN => "errreturn",
x if x == zh::EXTENDEDGLOB => "extendedglob",
x if x == zh::EXTENDEDHISTORY => "extendedhistory",
x if x == zh::FLOWCONTROL => "flowcontrol",
x if x == zh::FORCEFLOAT => "forcefloat",
x if x == zh::FUNCTIONARGZERO => "functionargzero",
x if x == zh::GLOBOPT => "glob",
x if x == zh::GLOBALEXPORT => "globalexport",
x if x == zh::GLOBALRCS => "globalrcs",
x if x == zh::GLOBASSIGN => "globassign",
x if x == zh::GLOBCOMPLETE => "globcomplete",
x if x == zh::GLOBDOTS => "globdots",
x if x == zh::GLOBSTARSHORT => "globstarshort",
x if x == zh::GLOBSUBST => "globsubst",
x if x == zh::HASHCMDS => "hashcmds",
x if x == zh::HASHDIRS => "hashdirs",
x if x == zh::HASHEXECUTABLESONLY => "hashexecutablesonly",
x if x == zh::HASHLISTALL => "hashlistall",
x if x == zh::HISTALLOWCLOBBER => "histallowclobber",
x if x == zh::HISTBEEP => "histbeep",
x if x == zh::HISTEXPIREDUPSFIRST => "histexpiredupsfirst",
x if x == zh::HISTFCNTLLOCK => "histfcntllock",
x if x == zh::HISTFINDNODUPS => "histfindnodups",
x if x == zh::HISTIGNOREALLDUPS => "histignorealldups",
x if x == zh::HISTIGNOREDUPS => "histignoredups",
x if x == zh::HISTIGNORESPACE => "histignorespace",
x if x == zh::HISTLEXWORDS => "histlexwords",
x if x == zh::HISTNOFUNCTIONS => "histnofunctions",
x if x == zh::HISTNOSTORE => "histnostore",
x if x == zh::HISTREDUCEBLANKS => "histreduceblanks",
x if x == zh::HISTSAVEBYCOPY => "histsavebycopy",
x if x == zh::HISTSAVENODUPS => "histsavenodups",
x if x == zh::HISTSUBSTPATTERN => "histsubstpattern",
x if x == zh::HISTVERIFY => "histverify",
x if x == zh::HUP => "hup",
x if x == zh::IGNOREBRACES => "ignorebraces",
x if x == zh::IGNORECLOSEBRACES => "ignoreclosebraces",
x if x == zh::IGNOREEOF => "ignoreeof",
x if x == zh::INCAPPENDHISTORY => "incappendhistory",
x if x == zh::INCAPPENDHISTORYTIME => "incappendhistorytime",
x if x == zh::INTERACTIVE => "interactive",
x if x == zh::INTERACTIVECOMMENTS => "interactivecomments",
x if x == zh::KSHARRAYS => "ksharrays",
x if x == zh::KSHAUTOLOAD => "kshautoload",
x if x == zh::KSHGLOB => "kshglob",
x if x == zh::KSHOPTIONPRINT => "kshoptionprint",
x if x == zh::KSHTYPESET => "kshtypeset",
x if x == zh::KSHZEROSUBSCRIPT => "kshzerosubscript",
x if x == zh::LISTAMBIGUOUS => "listambiguous",
x if x == zh::LISTBEEP => "listbeep",
x if x == zh::LISTPACKED => "listpacked",
x if x == zh::LISTROWSFIRST => "listrowsfirst",
x if x == zh::LISTTYPES => "listtypes",
x if x == zh::LOCALLOOPS => "localloops",
x if x == zh::LOCALOPTIONS => "localoptions",
x if x == zh::LOCALPATTERNS => "localpatterns",
x if x == zh::LOCALTRAPS => "localtraps",
x if x == zh::LOGINSHELL => "loginshell",
x if x == zh::LONGLISTJOBS => "longlistjobs",
x if x == zh::MAGICEQUALSUBST => "magicequalsubst",
x if x == zh::MAILWARNING => "mailwarning",
x if x == zh::MARKDIRS => "markdirs",
x if x == zh::MENUCOMPLETE => "menucomplete",
x if x == zh::MONITOR => "monitor",
x if x == zh::MULTIBYTE => "multibyte",
x if x == zh::MULTIFUNCDEF => "multifuncdef",
x if x == zh::MULTIOS => "multios",
x if x == zh::NOMATCH => "nomatch",
x if x == zh::NOTIFY => "notify",
x if x == zh::NULLGLOB => "nullglob",
x if x == zh::NUMERICGLOBSORT => "numericglobsort",
x if x == zh::OCTALZEROES => "octalzeroes",
x if x == zh::OVERSTRIKE => "overstrike",
x if x == zh::PATHDIRS => "pathdirs",
x if x == zh::PATHSCRIPT => "pathscript",
x if x == zh::PIPEFAIL => "pipefail",
x if x == zh::POSIXALIASES => "posixaliases",
x if x == zh::POSIXARGZERO => "posixargzero",
x if x == zh::POSIXBUILTINS => "posixbuiltins",
x if x == zh::POSIXCD => "posixcd",
x if x == zh::POSIXIDENTIFIERS => "posixidentifiers",
x if x == zh::POSIXJOBS => "posixjobs",
x if x == zh::POSIXSTRINGS => "posixstrings",
x if x == zh::POSIXTRAPS => "posixtraps",
x if x == zh::PRINTEIGHTBIT => "printeightbit",
x if x == zh::PRINTEXITVALUE => "printexitvalue",
x if x == zh::PRIVILEGED => "privileged",
x if x == zh::PROMPTBANG => "promptbang",
x if x == zh::PROMPTCR => "promptcr",
x if x == zh::PROMPTPERCENT => "promptpercent",
x if x == zh::PROMPTSP => "promptsp",
x if x == zh::PROMPTSUBST => "promptsubst",
x if x == zh::PUSHDIGNOREDUPS => "pushdignoredups",
x if x == zh::PUSHDMINUS => "pushdminus",
x if x == zh::PUSHDSILENT => "pushdsilent",
x if x == zh::PUSHDTOHOME => "pushdtohome",
x if x == zh::RCEXPANDPARAM => "rcexpandparam",
x if x == zh::RCQUOTES => "rcquotes",
x if x == zh::RCS => "rcs",
x if x == zh::RECEXACT => "recexact",
x if x == zh::REMATCHPCRE => "rematchpcre",
x if x == zh::RESTRICTED => "restricted",
x if x == zh::RMSTARSILENT => "rmstarsilent",
x if x == zh::RMSTARWAIT => "rmstarwait",
x if x == zh::SHAREHISTORY => "sharehistory",
x if x == zh::SHFILEEXPANSION => "shfileexpansion",
x if x == zh::SHGLOB => "shglob",
x if x == zh::SHINSTDIN => "shinstdin",
x if x == zh::SHNULLCMD => "shnullcmd",
x if x == zh::SHOPTIONLETTERS => "shoptionletters",
x if x == zh::SHORTLOOPS => "shortloops",
x if x == zh::SHORTREPEAT => "shortrepeat",
x if x == zh::SHWORDSPLIT => "shwordsplit",
x if x == zh::SINGLECOMMAND => "singlecommand",
x if x == zh::SINGLELINEZLE => "singlelinezle",
x if x == zh::SOURCETRACE => "sourcetrace",
x if x == zh::SUNKEYBOARDHACK => "sunkeyboardhack",
x if x == zh::TRANSIENTRPROMPT => "transientrprompt",
x if x == zh::TRAPSASYNC => "trapsasync",
x if x == zh::TYPESETSILENT => "typesetsilent",
x if x == zh::UNSET => "unset",
x if x == zh::VERBOSE => "verbose",
x if x == zh::VIMODE => "vimode",
x if x == zh::WARNCREATEGLOBAL => "warncreateglobal",
x if x == zh::WARNNESTEDVAR => "warnnestedvar",
x if x == zh::XTRACE => "xtrace",
x if x == zh::USEZLE => "zle",
_ => return None,
})
}