use std::collections::HashMap;
use std::sync::OnceLock;
use crate::ported::zsh_h::{PRINT_WHENCE_WORD, PRINT_WHENCE_CSH};
use crate::ported::zsh_h::EMULATE_ZSH;
use crate::ported::zsh_h::{options, MAX_OPS, XTRACE, BINF_KEEPNUM, ERRFLAG_ERROR};
use crate::ported::modules::parameter::DIRSTACK;
use std::sync::atomic::Ordering;
use crate::ported::zsh_h::{OPT_HASARG, OPT_ARG, PM_INTEGER, PM_EFLOAT, PM_FFLOAT};
use crate::ported::zsh_h::{PM_LEFT, PM_RIGHT_B, PM_RIGHT_Z};
use crate::ported::zsh_h::{OPT_MINUS, OPT_ISSET, PM_UNDEFINED};
use crate::ported::zsh_h::PM_LOADDIR;
use crate::ported::zsh_h::MFF_STR;
use crate::ported::zsh_h::{PM_ABSPATH_USED, FS_FUNC};
use crate::ported::zsh_h::eprog;
use crate::ported::zsh_h::{STAT_LOCKED, STAT_NOPRINT, STAT_STOPPED};
use std::io::Read;
use crate::ported::zsh_h::{OPT_PLUS, PM_UNALIASED, PM_TAGGED, PM_TAGGED_LOCAL, PM_WARNNESTED, PM_ZSHSTORED, PM_KSHSTORED, PM_CUR_FPATH};
use crate::ported::math::{matheval, mnumber, MN_INTEGER};
use crate::ported::utils::{getkeystring, getkeystring_with, quotedzputs, GETKEYS_PRINT};
use crate::ported::zsh_h::HIST_FOREIGN;
use crate::ported::zsh_h::{HFILE_APPEND, HFILE_SKIPOLD, HFILE_USE_OPTIONS};
use crate::ported::zsh_h::{EMULATION, TYPESET_OPTSTR, PM_HASHED, PM_HIDEVAL, PM_LOWER, PM_UPPER, PM_TIED, PM_LOCAL, PM_NAMEREF, PM_READONLY, PM_ARRAY, PRINT_TYPESET, PRINT_LINE, PRINT_TYPE, PRINT_NAMEONLY, PRINT_POSIX_EXPORT, PRINT_POSIX_READONLY, PRINT_WITH_NAMESPACE, EMULATE_KSH};
use crate::ported::zsh_h::{PRINT_WHENCE_VERBOSE, PRINT_WHENCE_SIMPLE, PRINT_WHENCE_FUNCDEF, PRINT_LIST};
use crate::ported::math::mathevali;
use crate::ported::zsh_h::DISABLED;
use crate::ported::zsh_h::nameddir;
use crate::ported::zsh_h::{ALIAS_GLOBAL, ALIAS_SUFFIX};
use crate::ported::hashtable::{aliastab_lock, sufaliastab_lock, Alias};
use crate::ported::zsh_h::{EMULATE_CSH, EMULATE_SH};
#[allow(unused_imports)]
use std::{env, fs, io, io::Write, path::Path, path::PathBuf};
#[allow(unused_imports)]
use indexmap::IndexMap;
#[allow(unused_imports)]
use crate::ported::exec::{
self, BUILTIN_NAMES,
format_int_in_base,
};
use crate::ported::utils::{zerr, zerrnam, zwarn, zwarnnam};
use crate::func_body_fmt::FuncBodyFmt;
#[allow(unused_imports)]
use crate::ported::options::ZSH_OPTIONS_SET;
#[allow(unused_imports)]
use crate::parse::{Redirect, ShellCommand};
#[allow(unused_imports)]
use crate::zwc::ZwcFile;
pub use crate::ported::hashtable_h::{
BIN_TYPESET, BIN_BG, BIN_FG, BIN_JOBS, BIN_WAIT, BIN_DISOWN,
BIN_BREAK, BIN_CONTINUE, BIN_EXIT, BIN_RETURN, BIN_CD,
BIN_POPD, BIN_PUSHD, BIN_PRINT, BIN_EVAL, BIN_SCHED, BIN_FC,
BIN_R, BIN_PUSHLINE, BIN_LOGOUT, BIN_TEST, BIN_BRACKET,
BIN_READONLY, BIN_ECHO, BIN_DISABLE, BIN_ENABLE, BIN_PRINTF,
BIN_COMMAND, BIN_UNHASH, BIN_UNALIAS, BIN_UNFUNCTION,
BIN_UNSET, BIN_EXPORT, BIN_SETOPT, BIN_UNSETOPT,
};
use crate::zsh_h::{builtin, BINF_ASSIGN, BINF_BUILTIN, BINF_COMMAND, BINF_DASH, BINF_DASHDASHVALID, BINF_EXEC, BINF_HANDLES_OPTS, BINF_MAGICEQUALS, BINF_NOGLOB, BINF_PLUSOPTS, BINF_PREFIX, BINF_PRINTOPTS, BINF_PSPECIAL, BINF_SKIPDASH, BINF_SKIPINVALID, hashnode, NULLBINCMD, isset};
#[allow(non_snake_case)]
pub fn BUILTIN(
name: &str,
flags: u32,
handler: Option<crate::ported::zsh_h::HandlerFunc>,
min: i32,
max: i32,
funcid: i32,
optstr: Option<&str>,
defopts: Option<&str>,
) -> builtin {
builtin {
node: hashnode {
next: None,
nam: name.to_string(),
flags: flags as i32,
},
handlerfunc: handler,
minargs: min,
maxargs: max,
funcid,
optstr: optstr.map(|s| s.to_string()),
defopts: defopts.map(|s| s.to_string()),
}
}
#[allow(non_snake_case)]
fn BIN_PREFIX(name: &str, flags: u32) -> builtin {
BUILTIN(name, flags | BINF_PREFIX, None, 0, 0, 0, None, None)
}
pub static BUILTINS: std::sync::LazyLock<Vec<builtin>> = std::sync::LazyLock::new(|| vec![
BIN_PREFIX("-", BINF_DASH),
BIN_PREFIX("builtin", BINF_BUILTIN),
BIN_PREFIX("command", BINF_COMMAND),
BIN_PREFIX("exec", BINF_EXEC),
BIN_PREFIX("noglob", BINF_NOGLOB),
BUILTIN("[", BINF_HANDLES_OPTS, Some(bin_test as crate::ported::zsh_h::HandlerFunc), 0, -1, BIN_BRACKET, None, None),
BUILTIN(".", BINF_PSPECIAL, Some(bin_dot as crate::ported::zsh_h::HandlerFunc), 1, -1, 0, None, None),
BUILTIN(":", BINF_PSPECIAL, Some(bin_true as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, None, None),
BUILTIN("alias", BINF_MAGICEQUALS | BINF_PLUSOPTS, Some(bin_alias as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("Lgmrs"), None),
BUILTIN("autoload", BINF_PLUSOPTS, None, 0, -1, 0, Some("dmktrRTUwWXz"), Some("u")),
BUILTIN("bg", 0, Some(crate::ported::jobs::bin_fg as crate::ported::zsh_h::HandlerFunc), 0, -1, BIN_BG, None, None),
BUILTIN("break", BINF_PSPECIAL, Some(bin_break as crate::ported::zsh_h::HandlerFunc), 0, 1, BIN_BREAK, None, None),
BUILTIN("bye", 0, None, 0, 1, BIN_EXIT, None, None),
BUILTIN("cd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, Some(bin_cd as crate::ported::zsh_h::HandlerFunc), 0, 2, BIN_CD, Some("qsPL"), None),
BUILTIN("chdir", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, Some(bin_cd as crate::ported::zsh_h::HandlerFunc), 0, 2, BIN_CD, Some("qsPL"), None),
BUILTIN("continue", BINF_PSPECIAL, Some(bin_break as crate::ported::zsh_h::HandlerFunc), 0, 1, BIN_CONTINUE, None, None),
BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, Some(bin_typeset as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("AE:%F:%HL:%R:%TUZ:%afghi:%klmnp:%rtuxz"), None),
BUILTIN("dirs", 0, Some(bin_dirs as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("clpv"), None),
BUILTIN("disable", 0, Some(bin_enable as crate::ported::zsh_h::HandlerFunc), 0, -1, BIN_DISABLE, Some("afmprs"), None),
BUILTIN("disown", 0, Some(crate::ported::jobs::bin_fg as crate::ported::zsh_h::HandlerFunc), 0, -1, BIN_DISOWN, None, None),
BUILTIN("echo", BINF_SKIPINVALID, Some(bin_print as crate::ported::zsh_h::HandlerFunc), 0, -1, BIN_ECHO, Some("neE"), Some("-")),
BUILTIN("emulate", 0, Some(bin_emulate as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("lLR"), None),
BUILTIN("enable", 0, Some(bin_enable as crate::ported::zsh_h::HandlerFunc), 0, -1, BIN_ENABLE, Some("afmprs"), None),
BUILTIN("eval", BINF_PSPECIAL, Some(bin_eval as crate::ported::zsh_h::HandlerFunc), 0, -1, BIN_EVAL, None, None),
BUILTIN("exit", BINF_PSPECIAL, Some(bin_break as crate::ported::zsh_h::HandlerFunc), 0, 1, BIN_EXIT, None, None),
BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, Some(bin_typeset as crate::ported::zsh_h::HandlerFunc), 0, -1, BIN_EXPORT, Some("E:%F:%HL:%R:%TUZ:%afhi:%lp:%rtu"), Some("xg")),
BUILTIN("false", 0, Some(bin_false as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, None, None),
BUILTIN("fc", 0, None, 0, -1, BIN_FC, Some("aAdDe:EfiIlLmnpPrRst:W"), None),
BUILTIN("fg", 0, None, 0, -1, BIN_FG, None, None),
BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, Some(bin_typeset as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("E:%F:%HL:%R:%Z:%ghlp:%rtux"), Some("E")),
BUILTIN("functions", BINF_PLUSOPTS, Some(bin_functions as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("ckmMstTuUWx:z"), None),
BUILTIN("getln", 0, None, 0, -1, 0, Some("ecnAlE"), Some("zr")),
BUILTIN("getopts", 0, Some(bin_getopts as crate::ported::zsh_h::HandlerFunc), 2, -1, 0, None, None),
BUILTIN("hash", BINF_MAGICEQUALS, Some(bin_hash as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("Ldfmrv"), None),
BUILTIN("hashinfo", 0, None, 0, 0, 0, None, None),
BUILTIN("history", 0, None, 0, -1, BIN_FC, Some("adDEfiLmnpPrt:"), Some("l")),
BUILTIN("integer", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, Some(bin_typeset as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("HL:%R:%Z:%ghi:%lp:%rtux"), Some("i")),
BUILTIN("jobs", 0, Some(crate::ported::jobs::bin_fg as crate::ported::zsh_h::HandlerFunc), 0, -1, BIN_JOBS, Some("dlpZrs"), None),
BUILTIN("kill", BINF_HANDLES_OPTS, None, 0, -1, 0, None, None),
BUILTIN("let", 0, Some(bin_let as crate::ported::zsh_h::HandlerFunc), 1, -1, 0, None, None),
BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, Some(bin_typeset as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("AE:%F:%HL:%R:%TUZ:%ahi:%lnp:%rtux"), None),
BUILTIN("logout", 0, Some(bin_break as crate::ported::zsh_h::HandlerFunc), 0, 1, BIN_LOGOUT, None, None),
BUILTIN("mem", 0, None, 0, 0, 0, Some("v"), None),
BUILTIN("popd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, None, 0, 1, BIN_POPD, Some("q"), None),
BUILTIN("patdebug", 0, None, 1, -1, 0, Some("p"), None),
BUILTIN("print", BINF_PRINTOPTS, Some(bin_print as crate::ported::zsh_h::HandlerFunc), 0, -1, BIN_PRINT, Some("abcC:Df:ilmnNoOpPrRsSu:v:x:X:z-"), None),
BUILTIN("printf", BINF_SKIPINVALID | BINF_SKIPDASH, Some(bin_print as crate::ported::zsh_h::HandlerFunc), 1, -1, BIN_PRINTF, Some("v:"), None),
BUILTIN("pushd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, None, 0, 2, BIN_PUSHD, Some("qsPL"), None),
BUILTIN("pushln", 0, None, 0, -1, BIN_PRINT, None, Some("-nz")),
BUILTIN("pwd", 0, Some(bin_pwd as crate::ported::zsh_h::HandlerFunc), 0, 0, 0, Some("rLP"), None),
BUILTIN("r", 0, None, 0, -1, BIN_R, Some("IlLnr"), None),
BUILTIN("read", 0, Some(bin_read as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("cd:ek:%lnpqrst:%zu:AE"), None),
BUILTIN("readonly", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, Some(bin_typeset as crate::ported::zsh_h::HandlerFunc), 0, -1, BIN_READONLY, Some("AE:%F:%HL:%R:%TUZ:%afghi:%lptux"), Some("r")),
BUILTIN("rehash", 0, Some(bin_hash as crate::ported::zsh_h::HandlerFunc), 0, 0, 0, Some("df"), Some("r")),
BUILTIN("return", BINF_PSPECIAL, Some(bin_break as crate::ported::zsh_h::HandlerFunc), 0, 1, BIN_RETURN, None, None),
BUILTIN("set", BINF_PSPECIAL | BINF_HANDLES_OPTS, Some(bin_set as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, None, None),
BUILTIN("setopt", 0, None, 0, -1, BIN_SETOPT, None, None),
BUILTIN("shift", BINF_PSPECIAL, Some(bin_shift as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("p"), None),
BUILTIN("source", BINF_PSPECIAL, Some(bin_dot as crate::ported::zsh_h::HandlerFunc), 1, -1, 0, None, None),
BUILTIN("suspend", 0, None, 0, 0, 0, Some("f"), None),
BUILTIN("test", BINF_HANDLES_OPTS, Some(bin_test as crate::ported::zsh_h::HandlerFunc), 0, -1, BIN_TEST, None, None),
BUILTIN("ttyctl", 0, Some(bin_ttyctl as crate::ported::zsh_h::HandlerFunc), 0, 0, 0, Some("fu"), None),
BUILTIN("limit", 0, None, 0, -1, 0, Some("sh"), None), BUILTIN("ulimit", 0, None, 0, -1, 0, None, None), BUILTIN("unlimit", 0, None, 0, -1, 0, Some("hs"), None), BUILTIN("times", BINF_PSPECIAL, Some(bin_times as crate::ported::zsh_h::HandlerFunc), 0, 0, 0, None, None),
BUILTIN("trap", BINF_PSPECIAL | BINF_HANDLES_OPTS, Some(bin_trap as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, None, None),
BUILTIN("true", 0, Some(bin_true as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, None, None),
BUILTIN("type", 0, Some(bin_whence as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("ampfsSw"), Some("v")),
BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, Some(bin_typeset as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("AE:%F:%HL:%R:%TUZ:%afghi:%klp:%rtuxmnz"), None),
BUILTIN("umask", 0, Some(bin_umask as crate::ported::zsh_h::HandlerFunc), 0, 1, 0, Some("S"), None),
BUILTIN("unalias", 0, Some(bin_unhash as crate::ported::zsh_h::HandlerFunc), 0, -1, BIN_UNALIAS, Some("ams"), None),
BUILTIN("unfunction", 0, Some(bin_unhash as crate::ported::zsh_h::HandlerFunc), 1, -1, BIN_UNFUNCTION, Some("m"), Some("f")),
BUILTIN("unhash", 0, Some(bin_unhash as crate::ported::zsh_h::HandlerFunc), 1, -1, BIN_UNHASH, Some("adfms"), None),
BUILTIN("unset", BINF_PSPECIAL, Some(bin_unset as crate::ported::zsh_h::HandlerFunc), 1, -1, BIN_UNSET, Some("fmvn"), None),
BUILTIN("unsetopt", 0, None, 0, -1, BIN_UNSETOPT, None, None),
BUILTIN("wait", 0, Some(crate::ported::jobs::bin_fg as crate::ported::zsh_h::HandlerFunc), 0, -1, BIN_WAIT, None, None),
BUILTIN("whence", 0, Some(bin_whence as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("acmpvfsSwx:"), None),
BUILTIN("where", 0, Some(bin_whence as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("pmsSwx:"), Some("ca")),
BUILTIN("which", 0, Some(bin_whence as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("ampsSwx:"), Some("c")),
BUILTIN("zmodload", 0, Some(crate::ported::module::bin_zmodload as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("AFRILP:abcfdilmpsue"), None),
BUILTIN("zcompile", 0, None, 0, -1, 0, Some("tUMRcmzka"), None),
BUILTIN("zstyle", 0, Some(crate::ported::modules::zutil::bin_zstyle as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("LeLdgabsTtmnH"), None),
BUILTIN("zformat", 0, Some(crate::ported::modules::zutil::bin_zformat as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("Faf"), None),
BUILTIN("zparseopts", 0, Some(crate::ported::modules::zutil::bin_zparseopts as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("D-EFK-M-a:"), None),
BUILTIN("zregexparse", 0, Some(crate::ported::modules::zutil::bin_zregexparse as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("c"), None),
BUILTIN("cap", 0, Some(crate::ported::modules::cap::bin_cap as crate::ported::zsh_h::HandlerFunc), 0, 1, 0, None, None),
BUILTIN("getcap", 0, Some(crate::ported::modules::cap::bin_getcap as crate::ported::zsh_h::HandlerFunc), 1, -1, 0, None, None),
BUILTIN("setcap", 0, Some(crate::ported::modules::cap::bin_setcap as crate::ported::zsh_h::HandlerFunc), 1, -1, 0, None, None),
BUILTIN("pcre_compile", 0, Some(crate::ported::modules::pcre::bin_pcre_compile as crate::ported::zsh_h::HandlerFunc), 1, 1, 0, Some("aimx"), None),
BUILTIN("pcre_study", 0, Some(crate::ported::modules::pcre::bin_pcre_study as crate::ported::zsh_h::HandlerFunc), 0, 0, 0, None, None),
BUILTIN("pcre_match", 0, None, 1, -1, 0, Some("ab:nv:"), None),
BUILTIN("ztcp", 0, Some(crate::ported::modules::tcp::bin_ztcp as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("acdflLtv"), None),
BUILTIN("ztie", 0, Some(crate::ported::modules::db_gdbm::bin_ztie as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("d:f:r"), None),
BUILTIN("zuntie", 0, Some(crate::ported::modules::db_gdbm::bin_zuntie as crate::ported::zsh_h::HandlerFunc), 1, -1, 0, Some("u"), None),
BUILTIN("zgdbmpath", 0, Some(crate::ported::modules::db_gdbm::bin_zgdbmpath as crate::ported::zsh_h::HandlerFunc), 1, 1, 0, None, None),
BUILTIN("echoti", 0, Some(crate::ported::modules::terminfo::bin_echoti as crate::ported::zsh_h::HandlerFunc), 1, -1, 0, None, None),
BUILTIN("fg", 0, Some(crate::ported::jobs::bin_fg as crate::ported::zsh_h::HandlerFunc), 0, -1, BIN_FG, None, None),
BUILTIN("kill", BINF_HANDLES_OPTS, Some(crate::ported::jobs::bin_kill as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, None, None),
BUILTIN("suspend", 0, Some(crate::ported::jobs::bin_suspend as crate::ported::zsh_h::HandlerFunc), 0, 0, 0, Some("f"), None),
BUILTIN("bindkey", 0, Some(crate::ported::zle::zle_keymap::bin_bindkey as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("evaMldDANmrsLR"), None),
BUILTIN("vared", 0, Some(crate::ported::zle::zle_main::bin_vared as crate::ported::zsh_h::HandlerFunc), 1, 1, 0, Some("AaceghM:m:p:r:i:f:"), None),
BUILTIN("compadd", 0, Some(crate::ported::zle::complete::bin_compadd as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("J:V:1X:fnqQF:Wsi"), None),
BUILTIN("compset", 0, Some(crate::ported::zle::complete::bin_compset as crate::ported::zsh_h::HandlerFunc), 1, -1, 0, Some("npqPS:"), None),
BUILTIN("zle", 0, Some(crate::ported::zle::zle_thingy::bin_zle as crate::ported::zsh_h::HandlerFunc), 0, -1, 0, Some("aAcCDfFIKlLmMNRTU"), None),
BUILTIN("mkdir", 0, Some(crate::ported::modules::files::bin_mkdir as crate::ported::zsh_h::HandlerFunc), 1, -1, 0, Some("pm:"), None),
BUILTIN("rmdir", 0, Some(crate::ported::modules::files::bin_rmdir as crate::ported::zsh_h::HandlerFunc), 1, -1, 0, None, None),
BUILTIN("ln", 0, Some(crate::ported::modules::files::bin_ln as crate::ported::zsh_h::HandlerFunc), 1, -1, 0, Some("dfins"), None),
BUILTIN("rm", 0, Some(crate::ported::modules::files::bin_rm as crate::ported::zsh_h::HandlerFunc), 1, -1, 0, Some("dfiRrs"), None),
BUILTIN("chmod", 0, Some(crate::ported::modules::files::bin_chmod as crate::ported::zsh_h::HandlerFunc), 2, -1, 0, Some("Rs"), None),
BUILTIN("chown", 0, Some(crate::ported::modules::files::bin_chown as crate::ported::zsh_h::HandlerFunc), 2, -1, 0, Some("Rs"), None),
BUILTIN("sync", 0, Some(crate::ported::modules::files::bin_sync as crate::ported::zsh_h::HandlerFunc), 0, 0, 0, None, None),
]);
static builtintab: OnceLock<HashMap<String, &'static builtin>> = OnceLock::new();
pub static BUILTINS_DISABLED: std::sync::LazyLock< std::sync::Mutex<std::collections::HashSet<String>>
> = std::sync::LazyLock::new(|| {
std::sync::Mutex::new(std::collections::HashSet::new())
});
pub fn createbuiltintable() -> &'static HashMap<String, &'static builtin> { builtintab.get_or_init(|| {
let table: &'static Vec<builtin> = &*BUILTINS;
let watch_bintab: &'static Vec<builtin> =
&*crate::ported::modules::watch::bintab;
let mut m: HashMap<String, &'static builtin> =
HashMap::with_capacity(table.len() + watch_bintab.len());
for b in table.iter() {
m.insert(b.node.nam.clone(), b);
}
for b in watch_bintab.iter() {
m.insert(b.node.nam.clone(), b);
}
m
})
}
#[cfg(test)]
mod tests {
use crate::zsh_h::BINF_PREFIX;
use super::*;
#[test]
fn registration_table_matches_c_count() {
assert_eq!(BUILTINS.len(), 82);
}
#[test]
fn lookup_finds_known_builtins() {
for name in ["cd", "echo", "print", "fg", "bg", "jobs", "wait", "typeset", "test", "[", "."] {
assert!(createbuiltintable().get(name).copied().is_some(), "missing: {name}");
}
}
#[test]
fn lookup_misses_unknown() {
assert!(createbuiltintable().get("not-a-builtin-zZz").copied().is_none());
}
#[test]
fn prefix_entries_have_prefix_flag() {
for name in ["-", "builtin", "command", "exec", "noglob"] {
let b = createbuiltintable().get(name).copied().unwrap();
assert!(b.node.flags as u32 & BINF_PREFIX != 0, "{name} missing BINF_PREFIX");
}
}
#[test]
fn fixdir_canonicalizes_absolute_paths() {
assert_eq!(fixdir("/tmp/./foo"), "/tmp/foo");
assert_eq!(fixdir("/tmp//foo"), "/tmp/foo");
assert_eq!(fixdir("/tmp/bar/../foo"), "/tmp/foo");
assert_eq!(fixdir("/tmp/bar/baz/../.."), "/tmp");
}
#[test]
fn fixdir_drops_dotdot_past_root() {
assert_eq!(fixdir("/.."), "/");
assert_eq!(fixdir("/../.."), "/");
assert_eq!(fixdir("/foo/../../bar"), "/bar");
}
#[test]
fn fixdir_relative_keeps_leading_dotdot() {
assert_eq!(fixdir("../foo"), "../foo");
assert_eq!(fixdir("../../foo"), "../../foo");
assert_eq!(fixdir("foo/../bar"), "bar");
}
#[test]
fn fixdir_empty_collapses_to_dot() {
assert_eq!(fixdir("./"), ".");
assert_eq!(fixdir("foo/.."), ".");
}
#[test]
fn fixdir_empty_input_returns_empty() {
assert_eq!(fixdir(""), "");
}
#[test]
fn fg_dispatch_id_distinguishes_aliases() {
assert_eq!(createbuiltintable().get("fg").copied().unwrap().funcid, BIN_FG);
assert_eq!(createbuiltintable().get("bg").copied().unwrap().funcid, BIN_BG);
assert_eq!(createbuiltintable().get("jobs").copied().unwrap().funcid, BIN_JOBS);
assert_eq!(createbuiltintable().get("wait").copied().unwrap().funcid, BIN_WAIT);
assert_eq!(createbuiltintable().get("disown").copied().unwrap().funcid, BIN_DISOWN);
}
}
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AutoloadFlags: u32 {
const NO_ALIAS = 0b00000001; const ZSH_STYLE = 0b00000010; const KSH_STYLE = 0b00000100; const TRACE = 0b00001000; const USE_CALLER_DIR = 0b00010000; const LOADED = 0b00100000; }
}
pub fn printbuiltinnode(hn: *mut crate::ported::zsh_h::hashnode, printflags: i32) {
if hn.is_null() { return; }
let bn = unsafe { &*hn };
if (printflags & PRINT_WHENCE_WORD as i32) != 0 { println!("{}: builtin", bn.nam); return;
}
if (printflags & PRINT_WHENCE_CSH as i32) != 0 { println!("{}: shell built-in command", bn.nam); return;
}
println!("{}", bn.nam);
}
pub fn freebuiltinnode(hn: *mut crate::ported::zsh_h::hashnode) { if hn.is_null() { return; }
let bn = unsafe { &*hn };
if (bn.flags as u32 & crate::ported::zsh_h::BINF_ADDED) == 0 { }
}
pub fn init_builtins() { if !crate::ported::zsh_h::EMULATION(EMULATE_ZSH) { if let Ok(mut tab) = crate::ported::hashtable::reswdtab_lock().write() {
tab.disable("repeat");
}
}
}
pub const OPT_ALLOC_CHUNK: i32 = 16;
pub fn new_optarg(ops: &mut crate::ported::zsh_h::options) -> i32 { if ops.argscount == 63 { return 1;
}
if ops.argsalloc == ops.argscount { ops.args.resize((ops.argsalloc + OPT_ALLOC_CHUNK) as usize, String::new());
ops.argsalloc += OPT_ALLOC_CHUNK; }
ops.argscount += 1; 0 }
pub fn execbuiltin(args: Vec<String>, assigns: Vec<crate::ported::zsh_h::asgment>, bn: *mut crate::ported::zsh_h::builtin) -> i32 {
if bn.is_null() {
return 1;
}
let bn_ref = unsafe { &*bn };
let pp: Option<&str>; let name: String; let mut optstr: Option<String>; let mut flags: i32; let mut argc: i32; let mut execop: u8; let xtr: bool = isset(XTRACE);
let mut ops = options { ind: [0u8; MAX_OPS], args: Vec::new(), argscount: 0, argsalloc: 0 };
name = bn_ref.node.nam.clone();
if bn_ref.handlerfunc.is_none() { return 1; }
flags = bn_ref.node.flags; optstr = bn_ref.optstr.clone();
argc = args.len() as i32;
let argarr: Vec<String> = args; let mut argv: usize = 0;
if let Some(ref os) = optstr.clone() { let optstr_local = os.clone();
let mut optstr_bytes: Vec<u8> = optstr_local.into_bytes();
let mut skipinvalid = (flags & BINF_SKIPINVALID as i32) != 0;
loop {
let arg_str: String = match argarr.get(argv) {
Some(s) => s.clone(),
None => break,
};
let arg_bytes = arg_str.as_bytes();
if arg_bytes.is_empty() { break; }
let sense: i32 = if arg_bytes[0] == b'-' { 1 } else { 0 }; if sense == 0 && !((flags & BINF_PLUSOPTS as i32) != 0 && arg_bytes[0] == b'+') {
break;
}
if (flags & BINF_KEEPNUM as i32) == 0 && arg_bytes.len() >= 2
&& arg_bytes[1].is_ascii_digit() {
break;
}
if (flags & BINF_SKIPDASH as i32) != 0 && arg_bytes.len() == 1 { break;
}
if (flags & BINF_DASHDASHVALID as i32) != 0 && arg_str == "--" { argv += 1; break; }
if skipinvalid { let mut all_known = true;
for &c in &arg_bytes[1..] {
if !optstr_bytes.contains(&c) { all_known = false; break; }
}
if !all_known { break; } }
let mut k: usize = 1; if arg_bytes.len() >= 2 && arg_bytes[1] == b'-' { k = 2; }
if arg_bytes.len() == k { ops.ind[b'-' as usize] = 1; if sense == 0 { ops.ind[b'+' as usize] = 1; }
}
let mut bad_opt: Option<u8> = None;
while k < arg_bytes.len() { let c = arg_bytes[k];
execop = c; let optptr = optstr_bytes.iter().position(|&b| b == c); if let Some(optidx) = optptr { ops.ind[c as usize] = if sense != 0 { 1 } else { 2 }; if optidx + 1 < optstr_bytes.len() && optstr_bytes[optidx + 1] == b':' {
let mut argptr: Option<String> = None;
if optidx + 2 < optstr_bytes.len() && optstr_bytes[optidx + 2] == b':' {
if k + 1 < arg_bytes.len() { argptr = Some(String::from_utf8_lossy(&arg_bytes[k+1..]).into_owned()); }
} else if optidx + 2 < optstr_bytes.len() && optstr_bytes[optidx + 2] == b'%' {
if k + 1 < arg_bytes.len() && arg_bytes[k+1].is_ascii_digit() {
argptr = Some(String::from_utf8_lossy(&arg_bytes[k+1..]).into_owned());
} else if let Some(nxt) = argarr.get(argv + 1) {
if !nxt.is_empty() && nxt.as_bytes()[0].is_ascii_digit() {
argv += 1; argptr = Some(nxt.clone());
}
}
} else {
if k + 1 < arg_bytes.len() { argptr = Some(String::from_utf8_lossy(&arg_bytes[k+1..]).into_owned()); } else if let Some(nxt) = argarr.get(argv + 1) {
argv += 1; argptr = Some(nxt.clone()); } else {
crate::ported::utils::zwarnnam(&name,
&format!("argument expected: -{}", execop as char)); return 1; }
}
if let Some(ap) = argptr { if new_optarg(&mut ops) != 0 { crate::ported::utils::zwarnnam(&name,
"too many option arguments"); return 1; }
ops.ind[execop as usize] |= (ops.argscount as u8) << 2;
ops.args[(ops.argscount - 1) as usize] = ap;
k = arg_bytes.len();
}
}
k += 1;
} else {
bad_opt = Some(c); break;
}
}
if let Some(badc) = bad_opt { crate::ported::utils::zwarnnam(&name,
&format!("bad option: {}{}",
if sense != 0 { '-' } else { '+' }, badc as char)); return 1; }
argv += 1; if (flags & BINF_PRINTOPTS as i32) != 0 && ops.ind[b'R' as usize] != 0
&& ops.ind[b'f' as usize] == 0 {
optstr_bytes = b"ne".to_vec(); flags |= BINF_SKIPINVALID as i32; skipinvalid = true;
}
if ops.ind[b'-' as usize] != 0 { break;
}
}
let _ = optstr_bytes;
} else if (flags & BINF_HANDLES_OPTS as i32) == 0 && argarr.get(argv).map(|s| s == "--").unwrap_or(false) { ops.ind[b'-' as usize] = 1; argv += 1; }
let _ = optstr.take();
pp = bn_ref.defopts.as_deref(); if let Some(pp_str) = pp { for &b in pp_str.as_bytes() { if ops.ind[b as usize] == 0 { ops.ind[b as usize] = 1; }
}
}
argc -= argv as i32;
let ef = crate::ported::utils::errflag.load(std::sync::atomic::Ordering::Relaxed);
if (ef & ERRFLAG_ERROR) != 0 { crate::ported::utils::errflag.fetch_and(!ERRFLAG_ERROR, std::sync::atomic::Ordering::Relaxed); return 1; }
if argc < bn_ref.minargs || (argc > bn_ref.maxargs && bn_ref.maxargs != -1) {
crate::ported::utils::zwarnnam(&name, if argc < bn_ref.minargs { "not enough arguments" }
else { "too many arguments" }); return 1; }
if xtr { let fullargv = &argarr; crate::ported::utils::printprompt4(); eprint!("{}", name); for s in fullargv { eprint!(" "); eprint!("{}", crate::ported::utils::quotedzputs(s)); }
for asg in &assigns { eprint!(" "); eprint!("{}", crate::ported::utils::quotedzputs(&asg.name)); if (asg.flags & crate::ported::zsh_h::ASG_ARRAY) != 0 { eprint!("=("); if let Some(ref list) = asg.array { if (asg.flags & crate::ported::zsh_h::ASG_KEY_VALUE) != 0 { let mut keynode = list.firstnode(); loop { let kidx = match keynode { Some(i) => i,
None => break, };
let vidx = match list.nextnode(kidx) { Some(i) => i,
None => break, };
eprint!("["); if let Some(k) = list.getdata(kidx) { eprint!("{}", crate::ported::utils::quotedzputs(k)); }
eprint!("]="); if let Some(v) = list.getdata(vidx) { eprint!("{}", crate::ported::utils::quotedzputs(v)); }
keynode = list.nextnode(vidx); }
} else { let mut arrnode = list.firstnode(); while let Some(idx) = arrnode { eprint!(" "); if let Some(elem) = list.getdata(idx) { eprint!("{}", crate::ported::utils::quotedzputs(elem)); }
arrnode = list.nextnode(idx); }
}
}
eprint!(" )"); } else if let Some(ref scalar) = asg.scalar { eprint!("="); eprint!("{}", crate::ported::utils::quotedzputs(scalar)); }
}
eprintln!(); }
let trimmed: Vec<String> = argarr[argv..].to_vec();
let handler = bn_ref.handlerfunc.expect("handlerfunc checked at c:264");
handler(&name, &trimmed, &ops, bn_ref.funcid) }
pub fn set_pwd_env() { let pwd = crate::ported::params::getsparam("PWD")
.or_else(|| std::env::current_dir().ok()
.map(|p| p.to_string_lossy().into_owned()));
if let Some(s) = pwd {
crate::ported::params::setsparam("PWD", &s); std::env::set_var("PWD", &s);
}
if let Some(s) = crate::ported::params::getsparam("OLDPWD") {
std::env::set_var("OLDPWD", &s);
}
}
pub fn cd_get_dest(nam: &str, argv: &[String], _hard: bool, func: i32) -> Option<String> {
if argv.is_empty() { if func == BIN_POPD {
let depth = DIRSTACK.lock().map(|d| d.len()).unwrap_or(0);
if depth < 2 { crate::ported::utils::zwarnnam(nam, "directory stack empty"); return None; }
return DIRSTACK.lock().ok()
.and_then(|d| d.get(1).cloned());
}
if func == BIN_PUSHD {
let pushdtohome = crate::ported::zsh_h::isset(crate::ported::options::optlookup("pushdtohome"));
if !pushdtohome { return DIRSTACK.lock().ok()
.and_then(|d| d.get(1).cloned());
}
}
match crate::ported::params::getsparam("HOME") {
Some(h) if !h.is_empty() => Some(h), _ => {
crate::ported::utils::zwarnnam(nam, "HOME not set"); None }
}
} else if argv.len() == 1 { let arg = &argv[0];
DOPRINTDIR.fetch_add(1, Ordering::Relaxed); let posixcd = crate::ported::zsh_h::isset(crate::ported::options::optlookup("posixcd"));
if !posixcd && arg.len() > 1
&& (arg.starts_with('+') || arg.starts_with('-'))
&& arg[1..].chars().all(|c| c.is_ascii_digit())
{
let dd: usize = arg[1..].parse().unwrap_or(0); let pushdminus = crate::ported::zsh_h::isset(crate::ported::options::optlookup("pushdminus"));
let from_top = (arg.starts_with('+')) ^ pushdminus; return DIRSTACK.lock().ok().and_then(|d| {
if from_top { d.get(dd).cloned() }
else if d.len() > dd { d.get(d.len() - 1 - dd).cloned() }
else { None }
});
}
if arg == "-" { DOPRINTDIR.fetch_sub(1, Ordering::Relaxed);
crate::ported::params::getsparam("OLDPWD")
} else {
Some(arg.clone()) }
} else {
let pwd = crate::ported::params::getsparam("PWD")
.unwrap_or_else(|| crate::ported::utils::zgetcwd().unwrap_or_default());
let pat = &argv[0];
let new_pat = &argv[1];
match pwd.find(pat.as_str()) { None => {
crate::ported::utils::zwarnnam(nam,
&format!("string not in pwd: {}", pat)); None }
Some(idx) => {
let mut out = String::new();
out.push_str(&pwd[..idx]); out.push_str(new_pat); out.push_str(&pwd[idx + pat.len()..]); DOPRINTDIR.fetch_add(1, Ordering::Relaxed);
Some(out)
}
}
}
}
pub fn cd_do_chdir(_cnam: &str, dest: &str, _hard: i32) -> Option<String> { match std::env::set_current_dir(dest) { Ok(_) => Some(dest.to_string()), Err(_) => None, }
}
pub fn cd_able_vars(s: &str) -> Option<String> { let cdablevars = crate::ported::zsh_h::isset(crate::ported::options::optlookup("cdablevars"));
if !cdablevars { return None;
}
let (head, tail) = match s.find('/') { Some(i) => (&s[..i], &s[i..]),
None => (s, ""),
};
if head.is_empty() {
return None;
}
crate::ported::params::getsparam(head)
.map(|val| format!("{}{}", val, tail))
}
#[allow(unused_variables)]
pub fn cd_try_chdir(pfix: &str, dest: &str, hard: i32) -> Option<String> { let buf = if pfix.is_empty() {
dest.to_string()
} else if pfix.ends_with('/') {
format!("{}{}", pfix, dest)
} else {
format!("{}/{}", pfix, dest) };
match std::env::set_current_dir(&buf) { Ok(_) => Some(buf),
Err(_) => None, }
}
pub fn cd_new_pwd(_func: i32, _dir: usize, _quiet: i32) { let _old = crate::ported::params::getsparam("PWD");
if let Ok(cwd) = std::env::current_dir() {
if let Some(s) = cwd.to_str() {
let _ = s;
}
}
}
pub fn printdirstack() { let pwd = crate::ported::params::getsparam("PWD")
.or_else(|| std::env::current_dir().ok()
.and_then(|p| p.to_str().map(String::from)))
.unwrap_or_default();
print!("{}", pwd);
if let Ok(d) = DIRSTACK.lock() {
for entry in d.iter() {
print!(" {}", entry); }
}
println!(); }
pub fn fixdir(src: &str) -> String { if src.is_empty() {
return String::new();
}
let abs = src.starts_with('/');
let mut components: Vec<&str> = Vec::new();
for seg in src.split('/') {
match seg {
"" => continue, "." => continue, ".." => {
if let Some(last) = components.last() {
if *last == ".." {
components.push("..");
} else {
components.pop();
}
} else if !abs {
components.push("..");
}
}
other => components.push(other),
}
}
let body = components.join("/");
if abs {
format!("/{}", body)
} else if body.is_empty() {
".".to_string()
} else {
body
}
}
pub fn printif(str: Option<&str>, c: u8) { if let Some(s) = str { print!(" -{} ", c as char); print!("{}", s); }
}
pub fn printqt(str: &str) { let rcquotes = crate::ported::zsh_h::isset(crate::ported::options::optlookup("rcquotes")); for ch in str.chars() { if ch == '\'' { print!("{}", if rcquotes { "''" } else { "'\\''" }); } else {
print!("{}", ch); }
}
}
pub fn fcgetcomm(s: &str) -> i64 { let trimmed = s.trim_start();
let numeric = trimmed.parse::<i64>().ok();
let is_zero_prefix = trimmed.starts_with('0');
if let Some(mut cmd) = numeric {
if cmd != 0 || is_zero_prefix {
if cmd < 0 {
let curh = crate::ported::hist::curhist.load(
std::sync::atomic::Ordering::Relaxed);
cmd = crate::ported::hist::addhistnum(curh, cmd as i32, 1);
}
if cmd < 0 { cmd = 0;
}
return cmd;
}
}
match crate::ported::hist::hcomsearch(s) {
Some(n) => n,
None => {
crate::ported::utils::zwarnnam(
"fc", &format!("event not found: {}", s));
-1
}
}
}
pub fn fcsubs(sp: &mut String, sub: &[(String, String)]) -> i32 { let mut subbed = 0i32; for (old, new) in sub { if old.is_empty() {
continue;
}
let count = sp.matches(old.as_str()).count() as i32; if count > 0 {
*sp = sp.replace(old.as_str(), new); subbed += count;
}
}
subbed
}
#[allow(clippy::too_many_arguments)]
pub fn fclist(out: &mut dyn std::io::Write, ops: &crate::ported::zsh_h::options,
mut first: i64, mut last: i64,
subs: &[(String, String)],
pprog: Option<&str>,
is_command: i32) -> i32 {
use std::io::Write;
if OPT_ISSET(ops, b'r') {
std::mem::swap(&mut first, &mut last);
}
if is_command != 0 && first > last {
crate::ported::utils::zwarnnam(
"fc",
"history events can't be executed backwards, aborted",
);
return 1;
}
let near = if first < last { 1 } else { -1 };
let start_ev = match crate::ported::hist::gethistent(first, near) {
Some(e) => e,
None => {
crate::ported::utils::zwarnnam(
"fc",
if first == last {
"no such event"
} else {
"no events in that range"
},
);
return 1;
}
};
let want_time = OPT_ISSET(ops, b'd') || OPT_ISSET(ops, b'f')
|| OPT_ISSET(ops, b'E') || OPT_ISSET(ops, b'i')
|| OPT_ISSET(ops, b't');
let tdfmt: Option<&'static str> = if !want_time {
None
} else if OPT_ISSET(ops, b't') {
Some("%H:%M") } else if OPT_ISSET(ops, b'i') {
Some("%Y-%m-%d %H:%M")
} else if OPT_ISSET(ops, b'E') {
Some("%d.%m.%Y %H:%M")
} else if OPT_ISSET(ops, b'f') {
Some("%m/%d/%Y %H:%M")
} else {
Some("%H:%M")
};
let mut ev = start_ev;
let step: i64 = if first < last { 1 } else { -1 };
loop {
let entry = match crate::ported::hist::quietgethist(ev) {
Some(e) => e,
None => break,
};
let line = entry.node.nam.clone();
if let Some(pat) = pprog {
let prog = crate::ported::pattern::patcompile(pat, 0, None);
let matched = prog.as_ref()
.map(|p| crate::ported::pattern::pattry(p, &line))
.unwrap_or(true);
if !matched {
if ev == last { break; }
ev += step;
continue;
}
}
let mut text = line;
for (old, new) in subs.iter() {
if old.is_empty() { continue; }
text = text.replace(old.as_str(), new.as_str());
}
if is_command == 0 {
if !OPT_ISSET(ops, b'n') {
let _ = write!(out, "{:>5}", ev);
if OPT_ISSET(ops, b'D') {
let _ = write!(out, "{:>10}", entry.stim - entry.ftim);
}
if let Some(fmt) = tdfmt {
let formatted: Option<String> = (|| {
let mut tm: libc::tm = unsafe { std::mem::zeroed() };
let t: libc::time_t = entry.stim as libc::time_t;
let cfmt = std::ffi::CString::new(fmt).ok()?;
unsafe {
if libc::localtime_r(&t, &mut tm).is_null() {
return None;
}
let mut buf = vec![0u8; 256];
let n = libc::strftime(
buf.as_mut_ptr() as *mut libc::c_char,
buf.len(),
cfmt.as_ptr(),
&tm,
);
if n == 0 { return None; }
buf.truncate(n);
String::from_utf8(buf).ok()
}
})();
if let Some(s) = formatted {
let _ = write!(out, " {}", s);
} else {
let _ = write!(out, " {}", entry.stim);
}
}
let _ = write!(out, " ");
}
}
let _ = writeln!(out, "{}", text);
if ev == last { break; }
ev += step;
if ev < 0 { break; }
}
0 }
pub fn fcedit(ename: &str, fn_: &str) -> i32 { if ename == "-" { return 1; }
let status = std::process::Command::new(ename) .arg(fn_)
.status();
match status {
Ok(s) => s.code().unwrap_or(1),
Err(_) => 1,
}
}
pub fn getasg(argvp: &mut Vec<String>, _assigns: &mut Vec<(String, String)>) -> Option<(String, String)> {
if argvp.is_empty() { return None;
}
let s = argvp.remove(0);
match s.find('=') { Some(i) => Some((s[..i].to_string(), s[i+1..].to_string())),
None => Some((s, String::new())), }
}
pub fn typeset_setbase(name: &str, pm: *mut crate::ported::zsh_h::param, ops: &crate::ported::zsh_h::options,
on: i32, always: i32) -> i32 {
let mut arg: Option<&str> = None; let on_u = on as u32;
if (on_u & PM_INTEGER) != 0 && OPT_HASARG(ops, b'i') { arg = OPT_ARG(ops, b'i'); } else if (on_u & PM_EFLOAT) != 0 && OPT_HASARG(ops, b'E') { arg = OPT_ARG(ops, b'E'); } else if (on_u & PM_FFLOAT) != 0 && OPT_HASARG(ops, b'F') { arg = OPT_ARG(ops, b'F'); }
if let Some(a) = arg { let base = match a.trim().parse::<i32>() {
Ok(b) => b,
Err(_) => {
if (on_u & PM_INTEGER) != 0 {
crate::ported::utils::zwarnnam(name, &format!("bad base value: {}", a)); } else {
crate::ported::utils::zwarnnam(name, &format!("bad precision value: {}", a)); }
return 1; }
};
if (on_u & PM_INTEGER) != 0 && (base < 2 || base > 36) { crate::ported::utils::zwarnnam(name, &format!("invalid base (must be 2 to 36 inclusive): {}", base)); return 1; }
if !pm.is_null() {
unsafe { (*pm).base = base; } }
} else if always != 0 { if !pm.is_null() {
unsafe { (*pm).base = 0; } }
}
0 }
pub fn typeset_setwidth(name: &str, pm: *mut crate::ported::zsh_h::param, ops: &crate::ported::zsh_h::options,
on: i32, always: i32) -> i32 {
let mut arg: Option<&str> = None; let on_u = on as u32;
if (on_u & PM_LEFT) != 0 && OPT_HASARG(ops, b'L') { arg = OPT_ARG(ops, b'L'); } else if (on_u & PM_RIGHT_B) != 0 && OPT_HASARG(ops, b'R') { arg = OPT_ARG(ops, b'R'); } else if (on_u & PM_RIGHT_Z) != 0 && OPT_HASARG(ops, b'Z') { arg = OPT_ARG(ops, b'Z'); }
if let Some(a) = arg { let width = match a.trim().parse::<i32>() {
Ok(w) => w,
Err(_) => {
crate::ported::utils::zwarnnam(name, &format!("bad width value: {}", a)); return 1; }
};
if !pm.is_null() {
unsafe { (*pm).width = width; } }
} else if always != 0 { if !pm.is_null() {
unsafe { (*pm).width = 0; } }
}
0 }
pub fn typeset_single(_cname: &str, _pname: &str, _pm: *mut crate::ported::zsh_h::param,
_func: i32, _on: i32, _off: i32, _roff: i32,
_asg: *mut crate::ported::zsh_h::asgment,
_altpm: *mut crate::ported::zsh_h::param,
_ops: &crate::ported::zsh_h::options,
_joinchar: i32)
-> *mut crate::ported::zsh_h::param {
std::ptr::null_mut()
}
pub fn eval_autoload(shf: *mut crate::ported::zsh_h::shfunc, name: &str, ops: &crate::ported::zsh_h::options, func: i32) -> i32 {
if shf.is_null() { return 1; }
let shf_mut = unsafe { &mut *shf };
if (shf_mut.node.flags as u32 & PM_UNDEFINED) == 0 { return 1; }
if shf_mut.funcdef.is_some() { shf_mut.funcdef = None; }
if OPT_MINUS(ops, b'X') { let fargv = vec![ crate::ported::utils::quotedzputs(name),
"\"$@\"".to_string(),
];
let p = mkautofn(shf); let _ = p; return bin_eval(name, &fargv, ops, func); }
let mode = if OPT_ISSET(ops, b'k') { 2 } else if OPT_ISSET(ops, b'z') { 0 } else { 1 };
let _d = OPT_ISSET(ops, b'd');
let r = crate::exec::loadautofn(shf, mode, 1, _d as i32); if r == 0 { 1 } else { 0 }
}
pub fn check_autoload(shf: *mut crate::ported::zsh_h::shfunc, name: &str, ops: &crate::ported::zsh_h::options, func: i32) -> i32 {
if OPT_ISSET(ops, b'X') { return eval_autoload(shf, name, ops, func); }
let want_r = OPT_ISSET(ops, b'r');
let want_R = OPT_ISSET(ops, b'R');
if (want_r || want_R) && !shf.is_null() { let shf_mut = unsafe { &mut *shf };
if (shf_mut.node.flags as u32 & PM_UNDEFINED) == 0 {
return 0;
}
if (shf_mut.node.flags as u32 & PM_LOADDIR) != 0
&& shf_mut.filename.is_some()
{
let spec = vec![shf_mut.filename.clone().unwrap_or_default()];
if crate::exec::getfpfunc(&shf_mut.node.nam, &mut None, Some(&spec), 1).is_some() {
return 0; }
if !OPT_ISSET(ops, b'd') { if want_R { crate::ported::utils::zerr(&format!(
"{}: function definition file not found",
shf_mut.node.nam)); return 1; }
return 0; }
}
let mut dir_path: Option<String> = None;
if crate::exec::getfpfunc(&shf_mut.node.nam, &mut dir_path, None, 1).is_some() && dir_path.is_some()
{
if let Some(old) = shf_mut.filename.take() {
crate::ported::hashtable::dircache_set(&old, None); }
let mut dp = dir_path.unwrap();
if !dp.starts_with('/') { if let Some(cwd) = crate::ported::utils::zgetcwd() {
dp = format!("{}/{}", cwd, dp); }
}
crate::ported::hashtable::dircache_set(&dp, Some(&dp)); shf_mut.filename = Some(dp);
shf_mut.node.flags |= PM_LOADDIR as i32; return 0; }
if want_R { crate::ported::utils::zerr(&format!(
"{}: function definition file not found",
shf_mut.node.nam)); return 1; }
}
0 }
pub fn listusermathfunc(p: &crate::ported::zsh_h::mathfunc) { let mut showargs: i32 = if p.module.is_some() { 3
} else if p.maxargs != if p.minargs != 0 { p.minargs } else { -1 } { 2
} else if p.minargs != 0 { 1
} else {
0 };
let s_suffix = if (p.flags & MFF_STR) != 0 { "s" } else { "" }; print!("functions -M{} {}", s_suffix, p.name); if showargs != 0 { print!(" {}", p.minargs); showargs -= 1; }
if showargs != 0 { print!(" {}", p.maxargs); showargs -= 1; }
if showargs != 0 { print!(" "); print!("{}", crate::ported::utils::quotedzputs(p.module.as_deref().unwrap_or(""))); showargs -= 1; }
println!(); }
pub fn add_autoload_function(shf: *mut crate::ported::zsh_h::shfunc, funcname: &str) {
if shf.is_null() || funcname.is_empty() { return; }
let shf_ref = unsafe { &mut *shf };
let is_abs_path = funcname.starts_with('/') && funcname.len() > 1
&& funcname[1..].contains('/')
&& (shf_ref.node.flags as u32 & PM_UNDEFINED) != 0;
if is_abs_path {
let nam_idx = funcname.rfind('/').unwrap(); let (dir, nam) = if nam_idx == 0 { ("/".to_string(), funcname[1..].to_string()) } else {
(funcname[..nam_idx].to_string(), funcname[nam_idx + 1..].to_string())
};
if let Some(old) = shf_ref.filename.take() {
crate::ported::hashtable::dircache_set(&old, None); }
crate::ported::hashtable::dircache_set(&dir, Some(&dir)); shf_ref.filename = Some(dir);
shf_ref.node.flags |= (PM_LOADDIR | PM_ABSPATH_USED) as i32; if let Ok(mut t) = shfunctab_table().lock() {
t.insert(nam, shf as usize); }
} else {
let calling_f: Option<String> = {
let stack = crate::ported::modules::parameter::FUNCSTACK
.lock().map(|s| s.clone()).unwrap_or_default();
stack.iter().rev().find(|fs| { FS_FUNC != 0 && !fs.name.is_empty()
&& (shf_ref.node.nam.is_empty() || fs.name != shf_ref.node.nam)
}).map(|fs| fs.name.clone()) };
if let Some(cf) = calling_f { let shf2_ptr = shfunctab_table().lock()
.ok()
.and_then(|t| t.get(&cf).copied())
.unwrap_or(0) as *mut crate::ported::zsh_h::shfunc;
if !shf2_ptr.is_null() {
let shf2 = unsafe { &*shf2_ptr };
let needs = (PM_LOADDIR | PM_ABSPATH_USED) as i32;
if (shf2.node.flags & needs) == needs { if let Some(dir2) = &shf2.filename { let buf = format!("{}/{}", dir2, funcname); if buf.len() <= libc::PATH_MAX as usize { let buf_c = std::ffi::CString::new(buf.clone()).ok();
if let Some(bc) = buf_c {
if unsafe { libc::access(bc.as_ptr(), libc::R_OK) } == 0 { if let Some(old) = shf_ref.filename.take() {
crate::ported::hashtable::dircache_set(&old, None); }
let dir2c = dir2.clone();
crate::ported::hashtable::dircache_set(&dir2c, Some(&dir2c)); shf_ref.filename = Some(dir2c);
shf_ref.node.flags |= (PM_LOADDIR | PM_ABSPATH_USED) as i32; }
}
}
}
}
}
}
if let Ok(mut t) = shfunctab_table().lock() {
t.insert(funcname.to_string(), shf as usize); }
}
}
static SHFUNCTAB_INNER: std::sync::OnceLock<std::sync::Mutex<std::collections::HashMap<String, usize>>>
= std::sync::OnceLock::new();
pub fn shfunctab_table() -> &'static std::sync::Mutex<std::collections::HashMap<String, usize>> {
SHFUNCTAB_INNER.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()))
}
pub fn mkautofn(shf: *mut crate::ported::zsh_h::shfunc) -> *mut crate::ported::zsh_h::eprog { let p = Box::new(eprog {
len: 5 * std::mem::size_of::<u32>() as i32, prog: Vec::new(), strs: None, shf: if shf.is_null() { None } else { Some(unsafe { Box::from_raw(shf) }) },
npats: 0, nref: 1, flags: 0,
pats: Vec::new(),
dump: None,
});
Box::into_raw(p)
}
pub fn fetchcmdnamnode(hn: *mut crate::ported::zsh_h::hashnode, _printflags: i32) {
if hn.is_null() { return; }
let cn = unsafe { &*hn };
if let Ok(mut m) = MATCHEDNODES.lock() {
m.push(cn.nam.clone()); }
}
pub static MATCHEDNODES: std::sync::Mutex<Vec<String>> =
std::sync::Mutex::new(Vec::new());
pub fn bin_true(_name: &str, _argv: &[String], _ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
0 }
pub fn bin_false(_name: &str, _argv: &[String], _ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
1 }
pub fn checkjobs() { let checkrunning = crate::ported::zsh_h::isset(crate::ported::options::optlookup("checkrunningjobs"));
let thisjob = THISJOB.load(Ordering::Relaxed);
let maxjob = MAXJOB.load(Ordering::Relaxed);
let mut found: Option<i32> = None;
let mut found_stat: i32 = 0;
for i in 1..=maxjob { let stat = JOBSTATS.lock()
.ok()
.and_then(|t| t.get(i as usize).copied())
.unwrap_or(0);
if i != thisjob && (stat & STAT_LOCKED) != 0 && (stat & STAT_NOPRINT) == 0 && (checkrunning || (stat & STAT_STOPPED) != 0) {
found = Some(i); found_stat = stat;
break;
}
}
if found.is_some() { if (found_stat & STAT_STOPPED) != 0 { crate::ported::utils::zerr("you have stopped jobs."); } else {
crate::ported::utils::zerr("you have running jobs."); }
STOPMSG.store(1, Ordering::Relaxed); }
}
pub static STOPMSG: std::sync::atomic::AtomicI32 =
std::sync::atomic::AtomicI32::new(0);
pub static SFCONTEXT: std::sync::atomic::AtomicI32 =
std::sync::atomic::AtomicI32::new(0); pub static MAXJOB: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static THISJOB: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static JOBSTATS: std::sync::Mutex<Vec<i32>> = std::sync::Mutex::new(Vec::new());
pub fn realexit() -> ! { let code = if SHELL_EXITING.load(std::sync::atomic::Ordering::Relaxed) != 0
|| EXIT_PENDING.load(std::sync::atomic::Ordering::Relaxed) != 0 {
EXIT_VAL.load(std::sync::atomic::Ordering::Relaxed)
} else {
LASTVAL.load(std::sync::atomic::Ordering::Relaxed)
};
std::process::exit(code); }
pub fn _realexit() -> ! { let code = if SHELL_EXITING.load(std::sync::atomic::Ordering::Relaxed) != 0
|| EXIT_PENDING.load(std::sync::atomic::Ordering::Relaxed) != 0 {
EXIT_VAL.load(std::sync::atomic::Ordering::Relaxed)
} else {
LASTVAL.load(std::sync::atomic::Ordering::Relaxed)
};
unsafe { libc::_exit(code) } }
pub static SHELL_EXITING: std::sync::atomic::AtomicI32 =
std::sync::atomic::AtomicI32::new(0);
pub static EXIT_PENDING: std::sync::atomic::AtomicI32 =
std::sync::atomic::AtomicI32::new(0);
pub static EXIT_VAL: std::sync::atomic::AtomicI32 =
std::sync::atomic::AtomicI32::new(0);
pub static LASTVAL: std::sync::atomic::AtomicI32 =
std::sync::atomic::AtomicI32::new(0);
#[allow(unused_variables)]
pub fn zexit(val: i32, from_where: i32) { EXIT_VAL.store(val, Ordering::Relaxed); if SHELL_EXITING.load(Ordering::Relaxed) == -1 { return;
}
SHELL_EXITING.store(1, Ordering::Relaxed);
realexit(); }
pub fn eval(argv: &[String]) -> i32 { if argv.is_empty() { return 0;
}
LASTVAL.load(std::sync::atomic::Ordering::Relaxed) }
pub fn zread(izle: i32, readchar: &mut i32, izle_timeout: i64) -> i32 { if izle != 0 { let _ = izle_timeout;
}
if *readchar >= 0 { let cc = *readchar as u8;
*readchar = -1; return cc as i32;
}
let mut buf = [0u8; 1];
let fd = {
use std::sync::atomic::Ordering;
let s = crate::ported::init::SHTTY.load(Ordering::Relaxed);
if s >= 0 { s } else { 0 } };
loop {
let n = unsafe {
libc::read(fd, buf.as_mut_ptr() as *mut libc::c_void, 1)
};
match n {
1 => return buf[0] as i32, 0 => return -1, -1 if std::io::Error::last_os_error().kind()
== std::io::ErrorKind::Interrupted => continue,
_ => return -1,
}
}
}
pub fn testlex() { if TEST_TOK.load(Ordering::Relaxed) == TEST_LEXERR { return;
}
let mut targs = TESTARGS.lock().unwrap_or_else(|e| {
TESTARGS.clear_poison(); e.into_inner()
});
let mut idx = TESTARGS_IDX.load(Ordering::Relaxed) as usize;
let cur = targs.get(idx).cloned(); if let Some(t) = cur.as_ref() {
if let Ok(mut ts) = TOKSTR.lock() { *ts = t.clone(); } }
let none = cur.is_none() || cur.as_deref() == Some("");
if none { let prev = TEST_TOK.load(Ordering::Relaxed);
TEST_TOK.store(if prev != 0 { TEST_NULLTOK } else { TEST_LEXERR }, Ordering::Relaxed);
return;
}
let arg = cur.unwrap();
let new_tok = match arg.as_str() { "-o" => TEST_DBAR, "-a" => TEST_DAMPER, "!" => TEST_BANG, "(" => TEST_INPAR, ")" => TEST_OUTPAR, "<" => TEST_INANG, ">" => TEST_OUTANG, _ => TEST_STRING, };
TEST_TOK.store(new_tok, Ordering::Relaxed);
idx += 1; TESTARGS_IDX.store(idx as i32, Ordering::Relaxed);
let _ = &mut *targs; }
pub static TEST_TOK: std::sync::atomic::AtomicI32 =
std::sync::atomic::AtomicI32::new(0);
const TEST_LEXERR: i32 = -1; const TEST_NULLTOK: i32 = 0;
const TEST_DBAR: i32 = 2; const TEST_DAMPER: i32 = 3; const TEST_BANG: i32 = 4; const TEST_INPAR: i32 = 5; const TEST_OUTPAR: i32 = 6; const TEST_INANG: i32 = 7; const TEST_OUTANG: i32 = 8; const TEST_STRING: i32 = 9;
pub static TESTARGS: std::sync::Mutex<Vec<String>> = std::sync::Mutex::new(Vec::new());
pub static TESTARGS_IDX: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static TOKSTR: std::sync::Mutex<String> = std::sync::Mutex::new(String::new());
pub fn bin_notavail(nam: &str, _argv: &[String], _ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
crate::ported::utils::zwarnnam(nam, "not available on this system"); 1 }
pub fn bin_functions(name: &str, argv: &[String], ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
let mut returnval: i32 = 0; let mut on: u32 = 0; let mut off: u32 = 0; let _pflags: i32 = 0; let _expand: i32 = 0;
if OPT_PLUS(ops, b'u') { off |= PM_UNDEFINED; } else if OPT_MINUS(ops, b'u') || OPT_ISSET(ops, b'X') { on |= PM_UNDEFINED; }
if OPT_MINUS(ops, b'U') { on |= PM_UNALIASED | PM_UNDEFINED; } else if OPT_PLUS(ops, b'U') { off |= PM_UNALIASED; }
if OPT_MINUS(ops, b't') { on |= PM_TAGGED; } else if OPT_PLUS(ops, b't') { off |= PM_TAGGED; }
if OPT_MINUS(ops, b'T') { on |= PM_TAGGED_LOCAL; } else if OPT_PLUS(ops, b'T') { off |= PM_TAGGED_LOCAL; }
if OPT_MINUS(ops, b'W') { on |= PM_WARNNESTED; } else if OPT_PLUS(ops, b'W') { off |= PM_WARNNESTED; }
let mut roff = off; if OPT_MINUS(ops, b'z') { on |= PM_ZSHSTORED; off |= PM_KSHSTORED; } else if OPT_PLUS(ops, b'z') { off |= PM_ZSHSTORED; roff |= PM_ZSHSTORED; }
if OPT_MINUS(ops, b'k') { on |= PM_KSHSTORED; off |= PM_ZSHSTORED; } else if OPT_PLUS(ops, b'k') { off |= PM_KSHSTORED; roff |= PM_KSHSTORED; }
if OPT_MINUS(ops, b'd') { on |= PM_CUR_FPATH; off |= PM_CUR_FPATH; } else if OPT_PLUS(ops, b'd') { off |= PM_CUR_FPATH; roff |= PM_CUR_FPATH; }
if (off & PM_UNDEFINED) != 0 || (OPT_ISSET(ops, b'k') && OPT_ISSET(ops, b'z')) || (OPT_ISSET(ops, b'x') && !OPT_HASARG(ops, b'x')) || (OPT_MINUS(ops, b'X') && OPT_ISSET(ops, b'm')) || (OPT_ISSET(ops, b'c')
&& (OPT_ISSET(ops, b'x') || OPT_ISSET(ops, b'X') || OPT_ISSET(ops, b'm')))
{
crate::ported::utils::zwarnnam(name, "invalid option(s)"); return 1; }
if OPT_ISSET(ops, b'c') { if argv.len() < 2 || argv.len() > 2 { crate::ported::utils::zwarnnam(name, "-c: requires two arguments"); return 1;
}
let src_name = &argv[0];
let dst_name = &argv[1];
let src_ptr = shfunctab_table().lock()
.ok()
.and_then(|t| t.get(src_name.as_str()).copied())
.unwrap_or(0) as *mut crate::ported::zsh_h::shfunc;
if src_ptr.is_null() { crate::ported::utils::zwarnnam(name,
&format!("no such function: {}", src_name)); return 1;
}
if (unsafe { (*src_ptr).node.flags } as u32 & PM_UNDEFINED) != 0 {
unsafe {
(*src_ptr).funcdef = None;
}
if crate::exec::loadautofn(src_ptr, 1, 0, 0) != 0 {
return 1;
}
}
let src_ref = unsafe { &*src_ptr };
let new_filename = if (src_ref.node.flags as u32 & PM_UNDEFINED) == 0
&& src_ref.filename.is_some()
{
src_ref.filename.clone() } else {
None
};
let _ = new_filename; if dst_name.starts_with("TRAP") { let sigidx = getsigidx(&dst_name[4..]); if sigidx != -1 { if crate::ported::signals::settrap(
sigidx,
None,
crate::ported::zsh_h::ZSIG_FUNC,
) != 0 { return 1; }
crate::ported::jobs::removetrapnode(sigidx); }
}
if let Ok(mut t) = shfunctab_table().lock() {
t.insert(dst_name.clone(), src_ptr as usize); }
return 0; }
let mut expand: i32 = 0; if OPT_ISSET(ops, b'x') { let arg = OPT_ARG(ops, b'x').unwrap_or("");
match arg.trim().parse::<i32>() { Ok(n) => {
expand = n; if expand == 0 { expand = -1; } }
Err(_) => {
crate::ported::utils::zwarnnam(name, "number expected after -x"); return 1; }
}
}
let mut pflags: i32 = 0;
if OPT_PLUS(ops, b'f') || roff != 0 || OPT_ISSET(ops, b'+') { pflags |= crate::ported::zsh_h::PRINT_NAMEONLY; }
if OPT_MINUS(ops, b'M') || OPT_PLUS(ops, b'M') { if on != 0 || off != 0 || pflags != 0
|| OPT_ISSET(ops, b'X') || OPT_ISSET(ops, b'u')
|| OPT_ISSET(ops, b'U') || OPT_ISSET(ops, b'w')
{
crate::ported::utils::zwarnnam(name, "invalid option(s)"); return 1; }
if argv.is_empty() { crate::ported::mem::queue_signals(); if let Ok(table) = crate::ported::module::MATHFUNCS.lock() { for p in table.iter() { if (p.flags & crate::ported::zsh_h::MFF_USERFUNC) != 0 { listusermathfunc(p); }
}
}
crate::ported::mem::unqueue_signals(); return returnval;
} else if OPT_ISSET(ops, b'm') { for arg in argv.iter() {
crate::ported::mem::queue_signals(); if let Some(pprog) = crate::ported::pattern::patcompile(
arg, crate::ported::zsh_h::PAT_STATIC, None,
) { if OPT_PLUS(ops, b'M') { if let Ok(mut table) =
crate::ported::module::MATHFUNCS.lock()
{
table.retain(|p| {
!((p.flags & crate::ported::zsh_h::MFF_USERFUNC) != 0
&& crate::ported::pattern::pattry(&pprog, &p.name))
});
}
} else {
if let Ok(table) = crate::ported::module::MATHFUNCS.lock() {
for p in table.iter() {
if (p.flags & crate::ported::zsh_h::MFF_USERFUNC) != 0
&& crate::ported::pattern::pattry(&pprog, &p.name)
{
listusermathfunc(p);
}
}
}
}
} else { crate::ported::utils::zwarnnam(name, &format!("bad pattern : {}", arg));
returnval = 1; }
crate::ported::mem::unqueue_signals(); }
return returnval;
} else if OPT_PLUS(ops, b'M') { for arg in argv.iter() {
crate::ported::mem::queue_signals(); if let Ok(mut table) = crate::ported::module::MATHFUNCS.lock() {
let idx = table.iter().position(|p| p.name == *arg); if let Some(i) = idx {
if (table[i].flags & crate::ported::zsh_h::MFF_USERFUNC) == 0 {
crate::ported::utils::zwarnnam(name, &format!("+M {}: is a library function", arg));
returnval = 1; } else {
table.remove(i); }
}
}
crate::ported::mem::unqueue_signals(); }
return returnval;
} else {
let mut argv_iter = argv.iter();
let funcname = argv_iter.next().unwrap(); let mut minargs: i32;
let mut maxargs: i32;
if OPT_ISSET(ops, b's') { minargs = 1; maxargs = 1; } else {
minargs = 0; maxargs = -1; }
let bytes = funcname.as_bytes();
let first_bad = bytes.is_empty()
|| (bytes[0] as char).is_ascii_digit()
|| !bytes.iter().all(|&c| c.is_ascii_alphanumeric() || c == b'_');
if first_bad { crate::ported::utils::zwarnnam(name, &format!("-M {}: bad math function name", funcname));
return 1; }
if let Some(arg) = argv_iter.next() { match arg.parse::<i32>() { Ok(n) if n >= 0 => minargs = n, _ => {
crate::ported::utils::zwarnnam(name, &format!("-M: invalid min number of arguments: {}", arg));
return 1; }
}
if OPT_ISSET(ops, b's') && minargs != 1 { crate::ported::utils::zwarnnam(name, "-Ms: must take a single string argument");
return 1; }
maxargs = minargs; }
if let Some(arg) = argv_iter.next() { match arg.parse::<i32>() { Ok(n) if n >= -1 && (n == -1 || n >= minargs) => maxargs = n,
_ => {
crate::ported::utils::zwarnnam(name, &format!("-M: invalid max number of arguments: {}", arg));
return 1; }
}
if OPT_ISSET(ops, b's') && maxargs != 1 { crate::ported::utils::zwarnnam(name, "-Ms: must take a single string argument");
return 1; }
}
let modname = argv_iter.next().cloned(); if argv_iter.next().is_some() { crate::ported::utils::zwarnnam(name, "-M: too many arguments"); return 1; }
let mut flags = crate::ported::zsh_h::MFF_USERFUNC; if OPT_ISSET(ops, b's') { flags |= crate::ported::zsh_h::MFF_STR; }
let new_fn = crate::ported::zsh_h::mathfunc {
next: None, name: funcname.clone(), flags, nfunc: None,
sfunc: None,
module: modname, minargs, maxargs, funcid: 0,
};
crate::ported::mem::queue_signals(); if let Ok(mut table) = crate::ported::module::MATHFUNCS.lock() {
if let Some(i) = table.iter().position(|p| p.name == new_fn.name) {
table.remove(i); }
table.insert(0, new_fn);
}
crate::ported::mem::unqueue_signals(); return returnval;
}
}
if OPT_MINUS(ops, b'X') { if argv.len() > 1 { crate::ported::utils::zwarnnam(name, "-X: too many arguments"); return 1; }
crate::ported::mem::queue_signals(); let funcname: Option<String> = {
let stack = crate::ported::modules::parameter::FUNCSTACK
.lock().map(|s| s.clone()).unwrap_or_default();
stack.iter().rev().find(|fs| !fs.name.is_empty()) .map(|fs| fs.name.clone()) };
let ret;
if funcname.is_none() { crate::ported::utils::zwarnnam(name, "bad autoload"); ret = 1; } else {
let fname = funcname.unwrap();
let shf_ptr = shfunctab_table().lock()
.ok()
.and_then(|t| t.get(fname.as_str()).copied())
.unwrap_or(0) as *mut crate::ported::zsh_h::shfunc;
if !shf_ptr.is_null() { } else {
if let Ok(mut t) = shfunctab_table().lock() {
t.insert(fname.clone(), 0); }
}
if !argv.is_empty() { if !shf_ptr.is_null() {
let shf_mut = unsafe { &mut *shf_ptr };
if let Some(old) = shf_mut.filename.take() {
crate::ported::hashtable::dircache_set(&old, None); }
crate::ported::hashtable::dircache_set(&argv[0],
Some(&argv[0])); shf_mut.filename = Some(argv[0].clone());
on |= PM_UNDEFINED >> 9 << 9; }
}
ret = eval_autoload(shf_ptr, &fname, ops, _func); }
crate::ported::mem::unqueue_signals(); return ret;
}
if argv.is_empty() { crate::ported::mem::queue_signals(); if OPT_ISSET(ops, b'U') && !OPT_ISSET(ops, b'u') { on &= !PM_UNDEFINED; }
crate::ported::mem::unqueue_signals(); return returnval;
}
if OPT_ISSET(ops, b'm') { on &= !PM_UNDEFINED; let mut returnval = returnval;
for pat in argv { crate::ported::mem::queue_signals(); let pprog = crate::ported::pattern::patcompile(pat, crate::ported::zsh_h::PAT_HEAPDUP, None);
if let Some(prog) = pprog {
if (on | off) == 0 && !OPT_ISSET(ops, b'X') { crate::ported::hashtable::scanmatchshfunc(
Some(pat),
|nm, _entry| println!("{}", nm),
);
} else {
let names: Vec<String> = shfunctab_table().lock()
.map(|t| t.keys().cloned().collect())
.unwrap_or_default();
for nm in &names {
if !crate::ported::pattern::pattry(&prog, nm) { continue;
}
let shf_ptr = shfunctab_table().lock()
.ok()
.and_then(|t| t.get(nm.as_str()).copied())
.unwrap_or(0) as *mut crate::ported::zsh_h::shfunc;
if shf_ptr.is_null() { continue; }
let shf_mut = unsafe { &mut *shf_ptr };
shf_mut.node.flags = (shf_mut.node.flags
| ((on & !PM_UNDEFINED) as i32)) & !(off as i32); if check_autoload(shf_ptr, &shf_mut.node.nam,
ops, _func) != 0 { returnval = 1; }
}
}
} else {
crate::ported::utils::zwarnnam(name,
&format!("bad pattern : {}", pat)); returnval = 1; }
crate::ported::mem::unqueue_signals(); }
return returnval;
}
let mut returnval = returnval;
crate::ported::mem::queue_signals(); for fname in argv { if OPT_ISSET(ops, b'w') { continue;
}
let shf_ptr = shfunctab_table().lock()
.ok()
.and_then(|t| t.get(fname.as_str()).copied())
.unwrap_or(0) as *mut crate::ported::zsh_h::shfunc;
if !shf_ptr.is_null() { let shf_mut = unsafe { &mut *shf_ptr };
if (on | off) != 0 { shf_mut.node.flags = (shf_mut.node.flags
| ((on & !PM_UNDEFINED) as i32)) & !(off as i32); if check_autoload(shf_ptr, &shf_mut.node.nam, ops, _func) != 0 { returnval = 1; }
} else {
println!("{}", shf_mut.node.nam); }
} else if (on & PM_UNDEFINED) != 0 { let mut sigidx: i32 = -1;
let mut ok = true;
if fname.starts_with("TRAP") { sigidx = getsigidx(&fname[4..]); if sigidx != -1 { crate::ported::jobs::removetrapnode(sigidx); }
}
if fname.starts_with('/') { let base = fname.rsplit('/').next().unwrap_or("");
if !base.is_empty() {
let base_ptr = shfunctab_table().lock()
.ok()
.and_then(|t| t.get(base).copied())
.unwrap_or(0) as *mut crate::ported::zsh_h::shfunc;
if !base_ptr.is_null() {
let bs = unsafe { &mut *base_ptr };
bs.node.flags = (bs.node.flags
| ((on & !PM_UNDEFINED) as i32)) & !(off as i32); if (bs.node.flags as u32 & PM_UNDEFINED) != 0 { let dir = if fname.len() > 1 && base.len() == fname.len() - 1 {
"/".to_string() } else {
fname[..fname.len() - base.len() - 1].to_string() };
if let Some(old) = bs.filename.take() {
crate::ported::hashtable::dircache_set(&old, None); }
crate::ported::hashtable::dircache_set(&dir, Some(&dir)); bs.filename = Some(dir);
}
if check_autoload(base_ptr, &bs.node.nam, ops, _func) != 0 { returnval = 1;
}
continue; }
}
}
let new_shf = Box::new(crate::ported::zsh_h::shfunc {
node: crate::ported::zsh_h::hashnode {
next: None,
nam: fname.clone(),
flags: on as i32, },
filename: None,
lineno: 0,
funcdef: None,
redir: None,
sticky: None,
body: None,
});
let new_shf_ptr = Box::into_raw(new_shf);
let _ = mkautofn(new_shf_ptr); add_autoload_function(new_shf_ptr, fname); if sigidx != -1 { if crate::ported::signals::settrap(
sigidx,
None,
crate::ported::zsh_h::ZSIG_FUNC,
) != 0 { if let Ok(mut t) = shfunctab_table().lock() {
t.remove(fname);
}
returnval = 1; ok = false; }
}
if ok && check_autoload(new_shf_ptr, &fname, ops, _func) != 0 { returnval = 1; }
} else {
returnval = 1; }
}
crate::ported::mem::unqueue_signals(); let _ = (expand, pflags);
returnval
}
pub fn bin_cd(nam: &str, argv: &[String], ops: &crate::ported::zsh_h::options, func: i32) -> i32 {
let prev = DOPRINTDIR.load(Ordering::Relaxed);
DOPRINTDIR.store(if prev == -1 { 1 } else { 0 }, Ordering::Relaxed);
let chase = OPT_ISSET(ops, b'P') || (crate::ported::zsh_h::isset(crate::ported::options::optlookup("chaselinks"))
&& !OPT_ISSET(ops, b'L'));
CHASINGLINKS.store(chase as i32, Ordering::Relaxed);
crate::ported::mem::queue_signals();
let pwd = crate::ported::params::getsparam("PWD")
.unwrap_or_else(|| crate::ported::utils::zgetcwd().unwrap_or_default());
if let Ok(mut d) = crate::ported::modules::parameter::DIRSTACK.lock() {
d.insert(0, pwd); }
let dest = cd_get_dest(nam, argv, OPT_ISSET(ops, b's'), func);
if dest.is_none() { if let Ok(mut d) = crate::ported::modules::parameter::DIRSTACK.lock() {
if !d.is_empty() { d.remove(0); } }
crate::ported::mem::unqueue_signals(); return 1; }
let dest_path = dest.unwrap();
let old = crate::ported::params::getsparam("PWD");
if std::env::set_current_dir(&dest_path).is_err() {
if let Ok(mut d) = crate::ported::modules::parameter::DIRSTACK.lock() {
if !d.is_empty() { d.remove(0); }
}
crate::ported::mem::unqueue_signals();
return 1;
}
if let Some(o) = old { crate::ported::params::setsparam("OLDPWD", &o);
std::env::set_var("OLDPWD", &o);
}
let chase = CHASINGLINKS.load(std::sync::atomic::Ordering::Relaxed) != 0; let pwd: String = if chase { match std::env::current_dir() {
Ok(c) => c.to_string_lossy().into_owned(),
Err(_) => dest_path.clone(),
}
} else {
dest_path.clone() };
crate::ported::params::setsparam("PWD", &pwd);
std::env::set_var("PWD", &pwd);
cd_new_pwd(func, 0, OPT_ISSET(ops, b'q') as i32);
crate::ported::mem::unqueue_signals(); 0 }
pub static DOPRINTDIR: std::sync::atomic::AtomicI32 =
std::sync::atomic::AtomicI32::new(0);
pub static CHASINGLINKS: std::sync::atomic::AtomicI32 =
std::sync::atomic::AtomicI32::new(0);
pub fn bin_pwd(_name: &str, _argv: &[String], ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
let chaselinks = crate::ported::zsh_h::isset(crate::ported::options::optlookup("chaselinks"));
if OPT_ISSET(ops, b'r') || OPT_ISSET(ops, b'P') || (chaselinks && !OPT_ISSET(ops, b'L')) {
println!("{}", crate::ported::utils::zgetcwd().unwrap_or_default()); } else {
println!("{}", std::env::var("PWD") .unwrap_or_else(|_|
crate::ported::utils::zgetcwd().unwrap_or_default()));
}
0 }
pub fn bin_shift(name: &str, argv: &[String], ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
let mut num: i32 = 1; let mut ret: i32 = 0; let mut idx = 0usize;
crate::ported::mem::queue_signals(); if !argv.is_empty() { let first = &argv[0];
let is_array = {
use crate::ported::zsh_h::{PM_ARRAY, PM_TYPE};
let tab = crate::ported::params::paramtab().read().unwrap();
tab.get(first)
.map(|pm| PM_TYPE(pm.node.flags as u32) == PM_ARRAY)
.unwrap_or(false)
};
if !is_array { num = first.trim().parse::<i32>().unwrap_or_else(|_| { ret = 1;
0
});
idx = 1;
if ret != 0 {
crate::ported::mem::unqueue_signals(); return 1;
}
}
}
if num < 0 { crate::ported::mem::unqueue_signals(); crate::ported::utils::zwarnnam(name,
"argument to shift must be non-negative"); return 1; }
if idx < argv.len() { for arr_name in &argv[idx..] { let s: Vec<String> = {
let tab = crate::ported::params::paramtab().read().unwrap();
match tab.get(arr_name).and_then(|pm| pm.u_arr.clone()) {
Some(arr) => arr,
None => continue,
}
};
if (s.len() as i32) < num { crate::ported::utils::zwarnnam(name,
"shift count must be <= $#"); ret += 1; continue; }
let s2: Vec<String> = if OPT_ISSET(ops, b'p') { s[..s.len() - num as usize].to_vec() } else {
s[num as usize..].to_vec() };
crate::ported::params::setaparam(arr_name, s2);
}
} else {
let mut pp = PPARAMS.lock().unwrap_or_else(|e| { PPARAMS.clear_poison(); e.into_inner() });
let l = pp.len() as i32;
if num > l { crate::ported::utils::zwarnnam(name, "shift count must be <= $#"); ret = 1; } else if OPT_ISSET(ops, b'p') { pp.truncate((l - num) as usize); } else {
pp.drain(..num as usize); }
drop(pp);
}
crate::ported::mem::unqueue_signals(); ret }
pub static PPARAMS: std::sync::Mutex<Vec<String>> =
std::sync::Mutex::new(Vec::new());
pub fn bin_let(_name: &str, argv: &[String], _ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
let mut val: mnumber = mnumber { l: 0, d: 0.0, type_: MN_INTEGER }; let mut had_error = false;
for expr in argv { match matheval(expr) { Ok(v) => val = v,
Err(_) => { had_error = true; break; }
}
}
if had_error { return 2; }
if val.type_ == MN_INTEGER { (val.l == 0) as i32
} else {
(val.d == 0.0) as i32
}
}
pub fn bin_times(_name: &str, _argv: &[String], _ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
let mut buf: libc::tms = unsafe { std::mem::zeroed() }; if unsafe { libc::times(&mut buf) } == (-1i64) as libc::clock_t { return 1; }
let clktck = unsafe { libc::sysconf(libc::_SC_CLK_TCK) } as f64;
let clktck = if clktck <= 0.0 { 100.0 } else { clktck };
let pttime = |t: libc::clock_t| {
let secs = t as f64 / clktck;
print!("{}m{:.3}s", (secs / 60.0) as i64, secs % 60.0);
};
pttime(buf.tms_utime); print!(" "); pttime(buf.tms_stime); println!(); pttime(buf.tms_cutime); print!(" "); pttime(buf.tms_cstime); println!(); 0 }
pub fn bin_eval(_name: &str, argv: &[String], _ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
eval(argv) }
pub fn bin_getopts(_name: &str, argv: &[String], _ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
if argv.len() < 2 { return 1; }
let optstr_full = argv[0].clone();
let var = argv[1].clone();
let argv_rest: Vec<String> = argv[2..].to_vec();
let args: Vec<String> = if !argv_rest.is_empty() {
argv_rest
} else {
PPARAMS.lock().map(|p| p.clone()).unwrap_or_default()
};
let mut zoptind = ZOPTIND.load(Ordering::Relaxed);
if zoptind < 1 { zoptind = 1;
OPTCIND.store(0, Ordering::Relaxed);
}
let mut optcind = OPTCIND.load(Ordering::Relaxed);
if (args.len() as i32) < zoptind { ZOPTIND.store(zoptind, Ordering::Relaxed);
return 1;
}
let (quiet, optstr) = if optstr_full.starts_with(':') { (true, &optstr_full[1..])
} else {
(false, optstr_full.as_str())
};
let mut str_buf = args[(zoptind - 1) as usize].clone();
let mut lenstr = str_buf.len() as i32;
if lenstr == 0 { return 1; }
if optcind >= lenstr { optcind = 0;
zoptind += 1;
if zoptind as usize > args.len() { ZOPTIND.store(zoptind, Ordering::Relaxed);
OPTCIND.store(optcind, Ordering::Relaxed);
return 1;
}
str_buf = args[(zoptind - 1) as usize].clone();
lenstr = str_buf.len() as i32;
}
if optcind == 0 { if lenstr < 2 || (!str_buf.starts_with('-') && !str_buf.starts_with('+')) {
ZOPTIND.store(zoptind, Ordering::Relaxed);
OPTCIND.store(optcind, Ordering::Relaxed);
return 1;
}
if lenstr == 2 && &str_buf[..2] == "--" { zoptind += 1;
ZOPTIND.store(zoptind, Ordering::Relaxed);
OPTCIND.store(0, Ordering::Relaxed);
return 1;
}
optcind += 1;
}
let opch = str_buf.as_bytes()[optcind as usize];
optcind += 1;
let plus = str_buf.starts_with('+');
let optbuf: String = if plus {
format!("+{}", opch as char)
} else {
format!("{}", opch as char)
};
let posix = crate::ported::zsh_h::isset(crate::ported::options::optlookup("posixbuiltins"));
let found = optstr.bytes().position(|b| b == opch);
if opch == b':' || found.is_none() { if posix { optcind = 0;
zoptind += 1;
}
crate::ported::params::setsparam(&var, "?");
if quiet { crate::ported::params::setsparam("OPTARG", &optbuf); } else {
let prefix = if plus { "+" } else { "-" };
crate::ported::utils::zwarn(&format!(
"bad option: {}{}", prefix, opch as char)); crate::ported::params::setsparam("OPTARG", "");
}
ZOPTIND.store(zoptind, Ordering::Relaxed);
OPTCIND.store(optcind, Ordering::Relaxed);
crate::ported::params::setiparam("OPTIND", zoptind as i64);
return 0;
}
let p = found.unwrap();
let optstr_bytes = optstr.as_bytes();
if p + 1 < optstr_bytes.len() && optstr_bytes[p + 1] == b':' { if optcind == lenstr { if zoptind as usize >= args.len() { if posix {
optcind = 0;
zoptind += 1;
}
if quiet { crate::ported::params::setsparam(&var, ":");
crate::ported::params::setsparam("OPTARG", &optbuf);
} else {
crate::ported::params::setsparam(&var, "?");
crate::ported::params::setsparam("OPTARG", "");
let prefix = if plus { "+" } else { "-" };
crate::ported::utils::zwarn(&format!(
"argument expected after {}{} option",
prefix, opch as char)); }
ZOPTIND.store(zoptind, Ordering::Relaxed);
OPTCIND.store(optcind, Ordering::Relaxed);
crate::ported::params::setiparam("OPTIND", zoptind as i64);
return 0;
}
let p_arg = args[zoptind as usize].clone();
zoptind += 1;
crate::ported::params::setsparam("OPTARG", &p_arg); optcind = 0;
} else {
let p_arg = str_buf[(optcind as usize)..].to_string();
crate::ported::params::setsparam("OPTARG", &p_arg);
optcind = 0;
zoptind += 1;
}
} else {
crate::ported::params::setsparam("OPTARG", "");
}
crate::ported::params::setsparam(&var, &optbuf);
ZOPTIND.store(zoptind, Ordering::Relaxed);
OPTCIND.store(optcind, Ordering::Relaxed);
crate::ported::params::setiparam("OPTIND", zoptind as i64);
0 }
pub static ZOPTIND: std::sync::atomic::AtomicI32 =
std::sync::atomic::AtomicI32::new(1);
pub static OPTCIND: std::sync::atomic::AtomicI32 =
std::sync::atomic::AtomicI32::new(0);
pub fn bin_read(name: &str, args: &[String], ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
let args = args.to_vec();
let mut nchars: i32 = 1;
if OPT_HASARG(ops, b'k') { let optarg = OPT_ARG(ops, b'k').unwrap_or("");
match optarg.trim().parse::<i32>() {
Ok(n) => nchars = n,
Err(_) => {
crate::ported::utils::zwarnnam(name,
&format!("number expected after -k: {}", optarg)); return 1;
}
}
}
let mut argi = 0usize;
let mut prompt: Option<String> = None;
if argi < args.len() && args[argi].starts_with('?') { prompt = Some(args[argi][1..].to_string());
argi += 1;
}
let want_array = OPT_ISSET(ops, b'A');
let reply = if argi < args.len() {
let r = args[argi].clone();
argi += 1;
r
} else if want_array {
"reply".to_string() } else {
"REPLY".to_string() };
if want_array && argi < args.len() { crate::ported::utils::zwarnnam(name, "only one array argument allowed"); return 1;
}
if OPT_ISSET(ops, b'l') || OPT_ISSET(ops, b'c') { return crate::ported::zle::compctl::compctlread(name, &args[argi..]);
}
let _ufd: i32 = if OPT_HASARG(ops, b'u') {
OPT_ARG(ops, b'u').and_then(|s| s.parse().ok()).unwrap_or(0)
} else { 0 };
if OPT_HASARG(ops, b't') {
let arg = OPT_ARG(ops, b't').unwrap_or("");
let tmout: f64 = arg.parse().unwrap_or(0.0);
let mut pfd = libc::pollfd { fd: 0, events: libc::POLLIN, revents: 0 };
let r = unsafe { libc::poll(&mut pfd, 1, (tmout * 1000.0) as i32) };
if r == 0 { return 4; } if r < 0 { return 2; } }
if let Some(ref p) = prompt {
eprint!("{}", p);
let _ = std::io::Write::flush(&mut std::io::stderr());
}
let mut buf = String::new();
if OPT_ISSET(ops, b'k') { let mut got = vec![0u8; nchars as usize];
let mut bytes_read = 0;
while bytes_read < nchars as usize {
let mut b = [0u8; 1];
match std::io::stdin().lock().read(&mut b) {
Ok(1) => { got[bytes_read] = b[0]; bytes_read += 1; }
_ => break,
}
}
buf = String::from_utf8_lossy(&got[..bytes_read]).into_owned();
} else {
match std::io::stdin().read_line(&mut buf) {
Ok(0) => return 1, Ok(_) => {
if buf.ends_with('\n') { buf.pop(); } }
Err(_) => return 2,
}
}
if want_array {
let parts: Vec<String> = buf.split_whitespace().map(String::from).collect();
crate::ported::params::setaparam(&reply, parts); } else if argi < args.len() {
let mut vars: Vec<String> = Vec::with_capacity(args.len() - argi + 1);
vars.push(reply);
for n in &args[argi..] { vars.push(n.clone()); }
let ifs = crate::ported::params::getsparam("IFS")
.unwrap_or_else(|| " \t\n".to_string());
let is_ifs = |c: char| ifs.contains(c);
let trimmed = buf.trim_start_matches(|c: char| is_ifs(c) && c.is_whitespace());
let mut remaining = trimmed.to_string();
for (i, var) in vars.iter().enumerate() {
if i + 1 == vars.len() {
let final_val = remaining.trim_end_matches(|c: char|
is_ifs(c) && c.is_whitespace()).to_string();
crate::ported::params::setsparam(var, &final_val);
} else {
match remaining.find(is_ifs) {
Some(idx) => {
let field = remaining[..idx].to_string();
let rest = &remaining[idx + remaining[idx..]
.chars().next().map(|c| c.len_utf8()).unwrap_or(1)..];
let rest = rest.trim_start_matches(|c: char|
is_ifs(c) && c.is_whitespace());
crate::ported::params::setsparam(var, &field);
remaining = rest.to_string();
}
None => {
crate::ported::params::setsparam(var, &remaining);
remaining.clear();
}
}
}
}
} else {
crate::ported::params::setsparam(&reply, &buf);
}
0
}
pub fn bin_print(name: &str, args: &[String], ops: &crate::ported::zsh_h::options, func: i32) -> i32 {
let nonewline = OPT_ISSET(ops, b'n'); let raw = OPT_ISSET(ops, b'r') || OPT_ISSET(ops, b'R'); let one_per_line = OPT_ISSET(ops, b'l'); let _printf_mode = func == BIN_PRINTF || OPT_HASARG(ops, b'f'); let echo_mode = func == BIN_ECHO;
let _ = (name, raw);
let dest_var: Option<String> = if OPT_HASARG(ops, b'v') {
OPT_ARG(ops, b'v').map(String::from)
} else { None };
if _printf_mode {
let fmt = if let Some(f) = OPT_ARG(ops, b'f') {
f.to_string()
} else if !args.is_empty() {
args[0].clone()
} else {
return 0;
};
let rest: &[String] = if OPT_HASARG(ops, b'f') { args } else { &args[1..] };
let out = printf_format(&fmt, rest);
if let Some(ref v) = dest_var {
crate::ported::params::setsparam(v, &out);
} else {
print!("{}", out);
}
return 0;
}
let sep = if one_per_line { "\n" } else { " " };
let mut processed_args: Vec<String> = if OPT_ISSET(ops, b'P') {
args.iter()
.map(|a| crate::ported::prompt::expand_prompt(a)) .collect()
} else {
args.to_vec()
};
if OPT_ISSET(ops, b'o') || OPT_ISSET(ops, b'O') {
let case_sensitive = OPT_ISSET(ops, b'i');
if case_sensitive {
processed_args.sort();
} else {
processed_args.sort_by_key(|s| s.to_lowercase());
}
if OPT_ISSET(ops, b'O') {
processed_args.reverse();
}
}
if !raw {
let echo_E = echo_mode && OPT_ISSET(ops, b'E');
if !echo_E {
for a in processed_args.iter_mut() {
let (s, _) = crate::ported::utils::getkeystring_with(a,
crate::ported::utils::GETKEYS_PRINT);
*a = s;
}
}
}
let body = processed_args.join(sep);
if let Some(ref v) = dest_var {
crate::ported::params::setsparam(v, &body);
} else {
print!("{}", body);
if !nonewline && !echo_mode {
println!();
} else if echo_mode && !nonewline {
println!();
}
}
0
}
fn printf_format(fmt: &str, args: &[String]) -> String {
let (fmt, _) = getkeystring(fmt); let mut out = String::new();
let mut arg_i: usize = 0;
loop {
let prev = arg_i;
let mut iter = fmt.chars().peekable();
while let Some(c) = iter.next() {
if c != '%' {
out.push(c);
continue;
}
let mut spec = String::from("%");
loop {
match iter.peek() {
Some(&c) if matches!(c, '-' | '+' | ' ' | '#' | '0') => {
spec.push(c); iter.next();
}
_ => break,
}
}
while let Some(&c) = iter.peek() {
if c.is_ascii_digit() { spec.push(c); iter.next(); }
else { break; }
}
if iter.peek() == Some(&'.') {
spec.push('.'); iter.next();
while let Some(&c) = iter.peek() {
if c.is_ascii_digit() { spec.push(c); iter.next(); }
else { break; }
}
}
match iter.next() {
Some('%') => out.push('%'),
Some('s') => {
let a = args.get(arg_i).cloned().unwrap_or_default();
spec.push('s');
out.push_str(&format_spec_str(&spec, &a));
arg_i += 1;
}
Some('d') | Some('i') => {
let a = args.get(arg_i).cloned().unwrap_or_default();
let n: i64 = a.parse().unwrap_or(0);
spec.push('d');
out.push_str(&format_spec_int(&spec, n));
arg_i += 1;
}
Some('u') => {
let a = args.get(arg_i).cloned().unwrap_or_default();
let n: u64 = a.parse().unwrap_or(0);
spec.push('u');
out.push_str(&format_spec_uint(&spec, n));
arg_i += 1;
}
Some('x') => {
let a = args.get(arg_i).cloned().unwrap_or_default();
let n: i64 = a.parse().unwrap_or(0);
spec.push('x');
out.push_str(&format!("{:x}", n));
arg_i += 1;
}
Some('X') => {
let a = args.get(arg_i).cloned().unwrap_or_default();
let n: i64 = a.parse().unwrap_or(0);
spec.push('X');
out.push_str(&format!("{:X}", n));
arg_i += 1;
}
Some('o') => {
let a = args.get(arg_i).cloned().unwrap_or_default();
let n: i64 = a.parse().unwrap_or(0);
spec.push('o');
out.push_str(&format!("{:o}", n));
arg_i += 1;
}
Some('f') | Some('F') | Some('g') | Some('G') | Some('e') | Some('E') => {
let a = args.get(arg_i).cloned().unwrap_or_default();
let n: f64 = a.parse().unwrap_or(0.0);
spec.push('f');
out.push_str(&format_spec_float(&spec, n));
arg_i += 1;
}
Some('c') => {
if let Some(a) = args.get(arg_i) {
if let Some(ch) = a.chars().next() { out.push(ch); }
}
arg_i += 1;
}
Some('q') => {
let a = args.get(arg_i).cloned().unwrap_or_default();
out.push_str("edzputs(&a));
arg_i += 1;
}
Some('b') => {
let a = args.get(arg_i).cloned().unwrap_or_default();
let (s, _) = getkeystring_with(&a, GETKEYS_PRINT);
out.push_str(&s);
arg_i += 1;
}
Some(other) => { out.push('%'); out.push(other); }
None => out.push('%'),
}
}
if arg_i == prev || arg_i >= args.len() { break; }
}
out
}
fn format_spec_str(spec: &str, s: &str) -> String {
let (left_align, width, prec) = parse_width_prec(spec);
let truncated: &str = if let Some(p) = prec {
let end: usize = s.chars().take(p).map(|c| c.len_utf8()).sum();
&s[..end.min(s.len())]
} else { s };
let pad = width.saturating_sub(truncated.chars().count());
if left_align {
format!("{}{}", truncated, " ".repeat(pad))
} else {
format!("{}{}", " ".repeat(pad), truncated)
}
}
fn format_spec_int(spec: &str, n: i64) -> String {
let (left_align, width, _prec) = parse_width_prec(spec);
let zero_pad = spec.contains('0') && !left_align;
let body = n.to_string();
let pad = width.saturating_sub(body.chars().count());
if pad == 0 { body }
else if left_align { format!("{}{}", body, " ".repeat(pad)) }
else if zero_pad {
if let Some(rest) = body.strip_prefix('-') {
format!("-{}{}", "0".repeat(pad), rest)
} else { format!("{}{}", "0".repeat(pad), body) }
} else { format!("{}{}", " ".repeat(pad), body) }
}
fn format_spec_uint(spec: &str, n: u64) -> String {
format_spec_int(spec, n as i64)
}
fn format_spec_float(spec: &str, n: f64) -> String {
let (left_align, width, prec) = parse_width_prec(spec);
let p = prec.unwrap_or(6);
let body = format!("{:.*}", p, n);
let pad = width.saturating_sub(body.chars().count());
if pad == 0 { body }
else if left_align { format!("{}{}", body, " ".repeat(pad)) }
else { format!("{}{}", " ".repeat(pad), body) }
}
fn parse_width_prec(spec: &str) -> (bool, usize, Option<usize>) {
let s = spec.trim_start_matches('%');
let mut i = 0;
let bytes = s.as_bytes();
let mut left_align = false;
while i < bytes.len() && matches!(bytes[i], b'-' | b'+' | b' ' | b'#' | b'0') {
if bytes[i] == b'-' { left_align = true; }
i += 1;
}
let width_start = i;
while i < bytes.len() && bytes[i].is_ascii_digit() { i += 1; }
let width: usize = s[width_start..i].parse().unwrap_or(0);
let mut prec: Option<usize> = None;
if i < bytes.len() && bytes[i] == b'.' {
i += 1;
let p_start = i;
while i < bytes.len() && bytes[i].is_ascii_digit() { i += 1; }
prec = Some(s[p_start..i].parse().unwrap_or(0));
}
(left_align, width, prec)
}
pub fn bin_fc(nam: &str, argv: &[String], ops: &mut crate::ported::zsh_h::options, func: i32) -> i32 {
let mut argv = argv.to_vec();
let mut first: i64 = -1;
let mut last: i64 = -1;
let mut asgf: Vec<(String, String)> = Vec::new();
if OPT_ISSET(ops, b'p') { let mut hf = "".to_string();
let mut hs: i64; let mut shs: i64; let level: i32 = if OPT_ISSET(ops, b'a') {
LOCALLEVEL.load(std::sync::atomic::Ordering::Relaxed)
} else { -1 };
hs = crate::ported::hist::histsiz.load(Ordering::Relaxed); shs = crate::ported::hist::savehistsiz.load(Ordering::Relaxed);
if !argv.is_empty() { hf = argv.remove(0); if !argv.is_empty() { let s2 = argv.remove(0);
match s2.parse::<i64>() { Ok(n) => hs = n,
Err(_) => {
crate::ported::utils::zwarnnam("fc", "HISTSIZE must be an integer");
return 1; }
}
if !argv.is_empty() { let s3 = argv.remove(0);
match s3.parse::<i64>() { Ok(n) => shs = n,
Err(_) => {
crate::ported::utils::zwarnnam("fc", "SAVEHIST must be an integer");
return 1; }
}
} else {
shs = hs; }
if !argv.is_empty() { crate::ported::utils::zwarnnam("fc", "too many arguments");
return 1; }
}
}
crate::ported::hist::pushhiststack(Some(&hf), hs, shs, level); if !hf.is_empty() { let exists = std::fs::metadata(&hf).is_ok();
let enoent = std::io::Error::last_os_error().raw_os_error()
== Some(libc::ENOENT);
if exists || !enoent { crate::ported::hist::readhistfile( Some(&hf), 1, HFILE_USE_OPTIONS as i32);
}
}
return 0; }
if OPT_ISSET(ops, b'P') { if !argv.is_empty() { crate::ported::utils::zwarnnam("fc", "too many arguments"); return 1; }
crate::ported::hist::saveandpophiststack(HFILE_USE_OPTIONS as i32); return 0;
}
let mut pprog: Option<crate::ported::pattern::PatProg> = None;
if !argv.is_empty() && OPT_ISSET(ops, b'm') { let pat = argv.remove(0);
match crate::ported::pattern::patcompile(&pat, crate::ported::zsh_h::PAT_HEAPDUP, None) {
Some(p) => pprog = Some(p),
None => {
crate::ported::utils::zwarnnam(nam, "invalid match pattern"); return 1; }
}
}
crate::ported::mem::queue_signals();
if OPT_ISSET(ops, b'R') { let path = argv.first().cloned();
let flags = if OPT_ISSET(ops, b'I') { HFILE_SKIPOLD as i32 } else { 0 };
crate::ported::hist::readhistfile( path.as_deref(), 1, flags);
crate::ported::mem::unqueue_signals(); return 0; }
if OPT_ISSET(ops, b'W') { let path = argv.first().cloned();
let flags = if OPT_ISSET(ops, b'I') { HFILE_SKIPOLD as i32 } else { 0 };
crate::ported::hist::savehistfile( path.as_deref(), flags);
crate::ported::mem::unqueue_signals(); return 0; }
if OPT_ISSET(ops, b'A') { let path = argv.first().cloned();
let mut flags = HFILE_APPEND as i32;
if OPT_ISSET(ops, b'I') { flags |= HFILE_SKIPOLD as i32; } crate::ported::hist::savehistfile( path.as_deref(), flags);
crate::ported::mem::unqueue_signals(); return 0; }
if crate::ported::builtins::sched::zleactive.load( std::sync::atomic::Ordering::Relaxed) != 0 {
crate::ported::mem::unqueue_signals(); crate::ported::utils::zwarnnam(nam, "no interactive history within ZLE");
return 1; }
while !argv.is_empty() && argv[0].contains('=') { let arg = argv.remove(0);
if let Some(eq) = arg.find('=') {
let n = &arg[..eq];
let v = &arg[eq + 1..];
if n.is_empty() {
crate::ported::utils::zwarnnam(nam,
&format!("invalid replacement pattern: ={}", v)); return 1;
}
asgf.push((n.to_string(), v.to_string())); }
}
if !argv.is_empty() { first = fcgetcomm(&argv.remove(0)); if first == -1 {
crate::ported::mem::unqueue_signals();
return 1; }
}
if !argv.is_empty() { last = fcgetcomm(&argv.remove(0)); if last == -1 {
crate::ported::mem::unqueue_signals();
return 1;
}
}
if !argv.is_empty() { crate::ported::mem::unqueue_signals();
crate::ported::utils::zwarnnam("fc", "too many arguments"); return 1;
}
let curhist: i64 = crate::ported::params::getiparam("HISTCMD");
if last == -1 { if OPT_ISSET(ops, b'l') && first < curhist { last = curhist; if last < 1 { last = 1; } } else {
last = first; }
}
if first == -1 { let _xflags = if OPT_ISSET(ops, b'L') { HIST_FOREIGN } else { 0 }; first = if OPT_ISSET(ops, b'l') { (curhist - 16).max(1) } else { (curhist - 1).max(1) };
if last < first { last = first; } }
let mut retval;
if OPT_ISSET(ops, b'l') { retval = fclist(&mut std::io::stdout(), ops, first, last,
&asgf, None, 0);
crate::ported::mem::unqueue_signals();
} else {
retval = 1; let fil_opt = crate::ported::utils::gettempfile(Some("zshfc")); match fil_opt {
None => { crate::ported::mem::unqueue_signals(); crate::ported::utils::zwarnnam("fc", &format!("can't open temp file: {}",
std::io::Error::last_os_error()));
}
Some((fd, fil)) => {
unsafe { libc::close(fd); } if last >= curhist { last = curhist - 1; if first > last { crate::ported::mem::unqueue_signals(); crate::ported::utils::zwarnnam("fc", "current history line would recurse endlessly, aborted");
let _ = std::fs::remove_file(&fil); return 1; }
}
ops.ind[b'n' as usize] = 1; let out = std::fs::OpenOptions::new()
.create(true).write(true).truncate(true).open(&fil).ok();
let listed = if let Some(mut f) = out { fclist(&mut f, ops, first, last, &asgf, None, 1)
} else { 1 };
if listed == 0 { let editor: String = if func == BIN_R || OPT_ISSET(ops, b's') {
"-".to_string() } else if OPT_HASARG(ops, b'e') { OPT_ARG(ops, b'e').unwrap_or("").to_string() } else {
crate::ported::params::getsparam("FCEDIT")
.or_else(|| crate::ported::params::getsparam("EDITOR"))
.unwrap_or_else(||
crate::ported::config_h::DEFAULT_FCEDIT.to_string())
};
crate::ported::mem::unqueue_signals(); if fcedit(&editor, &fil) != 0 { if crate::ported::input::stuff(&fil) != 0 { crate::ported::utils::zwarnnam("fc", &format!("{}: {}",
std::io::Error::last_os_error(), fil));
} else {
retval = LASTVAL.load( std::sync::atomic::Ordering::Relaxed);
}
}
} else {
crate::ported::mem::unqueue_signals(); }
let _ = std::fs::remove_file(&fil); }
}
}
let _ = pprog;
retval }
pub fn bin_typeset(name: &str, argv: &[String], ops: &crate::ported::zsh_h::options, func: i32) -> i32 {
#[cfg(feature = "recorder")]
if crate::recorder::is_enabled() {
let ctx = crate::recorder::recorder_ctx_global();
let mut letters = String::new();
let mut tied_mode = false;
for a in argv {
if a.starts_with('-') || a.starts_with('+') {
let body = &a[1..];
letters.push_str(body);
if body.contains('T') { tied_mode = true; }
}
}
let mut attrs = crate::recorder::ParamAttrs::from_flag_chars(&letters);
match func {
crate::ported::builtin::BIN_READONLY => {
attrs.set(crate::recorder::ParamAttrs::SCALAR);
attrs.set(crate::recorder::ParamAttrs::READONLY);
}
_ => {}
}
if func == crate::ported::builtin::BIN_EXPORT {
for a in argv {
if a == "-p" || a.starts_with('-') { continue; }
if let Some((k, v)) = a.split_once('=') {
crate::recorder::emit_export(k, Some(v), ctx.clone());
} else {
crate::recorder::emit_export(a, None, ctx.clone());
}
}
} else {
let is_locallike = matches!(name, "local" | "private");
let inside_function =
LOCALLEVEL.load(std::sync::atomic::Ordering::Relaxed) > 0;
if !is_locallike || !inside_function {
let mut tied_seen = 0usize;
for a in argv {
if a.starts_with('-') || a.starts_with('+') { continue; }
if tied_mode {
tied_seen += 1;
if tied_seen > 2 { break; }
}
if let Some((k, v)) = a.split_once('=') {
crate::recorder::emit_typeset_attrs(k, Some(v), attrs, ctx.clone());
} else {
crate::recorder::emit_typeset_attrs(a, None, attrs, ctx.clone());
}
}
}
}
}
let mut ops = ops.clone();
let mut on: u32 = 0; let mut off: u32 = 0; let returnval: i32 = 0; let mut printflags: i32 = PRINT_WITH_NAMESPACE; let hasargs = !argv.is_empty();
let posix = crate::ported::zsh_h::isset(crate::ported::options::optlookup("posixbuiltins"));
if (func == BIN_READONLY || func == BIN_EXPORT) && posix && hasargs { ops.ind[b'p' as usize] = 0; }
if OPT_ISSET(&ops, b'f') { return bin_functions(name, argv, &ops, func); }
if func == BIN_READONLY && posix && !OPT_PLUS(&ops, b'g') { ops.ind[b'g' as usize] = 1; }
let mut bit: u32 = PM_ARRAY; for ch in TYPESET_OPTSTR.chars() { let optval = ch as u8;
if OPT_MINUS(&ops, optval) { on |= bit; } else if OPT_PLUS(&ops, optval) { off |= bit; } else { bit <<= 1; continue; }
if OPT_MINUS(&ops, b'n')
&& (bit & !(PM_READONLY | PM_UPPER | PM_HIDEVAL)) != 0 {
crate::ported::utils::zwarnnam(name,
&format!("-{} not allowed with -n", ch)); }
bit <<= 1;
}
if OPT_MINUS(&ops, b'n') { if (on | off) & !(PM_READONLY | PM_UPPER | PM_HIDEVAL) != 0 { return 1; }
on |= PM_NAMEREF; } else if OPT_PLUS(&ops, b'n') { off |= PM_NAMEREF; }
let roff = off;
if (on & PM_FFLOAT) != 0 { off |= PM_UPPER | PM_ARRAY | PM_HASHED | PM_INTEGER | PM_EFLOAT; on &= !PM_EFLOAT; }
if (on & PM_EFLOAT) != 0 { off |= PM_UPPER | PM_ARRAY | PM_HASHED | PM_INTEGER | PM_FFLOAT; }
if (on & PM_INTEGER) != 0 { off |= PM_UPPER | PM_ARRAY | PM_HASHED | PM_EFLOAT | PM_FFLOAT; }
if (on & (PM_LEFT | PM_RIGHT_Z)) != 0 { off |= PM_RIGHT_B; }
if (on & PM_RIGHT_B) != 0 { off |= PM_LEFT | PM_RIGHT_Z; }
if (on & PM_UPPER) != 0 { off |= PM_LOWER; } if (on & PM_LOWER) != 0 { off |= PM_UPPER; } if (on & PM_HASHED) != 0 { off |= PM_ARRAY; } if (on & PM_TIED) != 0 { off |= PM_INTEGER | PM_EFLOAT | PM_FFLOAT | PM_ARRAY | PM_HASHED; }
on &= !off;
crate::ported::mem::queue_signals();
if OPT_ISSET(&ops, b'p') { if posix && !EMULATION(EMULATE_KSH) { printflags |= match func {
BIN_EXPORT => PRINT_POSIX_EXPORT, BIN_READONLY => PRINT_POSIX_READONLY, _ => PRINT_TYPESET, };
} else {
printflags |= PRINT_TYPESET; }
if OPT_HASARG(&ops, b'p') { let arg = OPT_ARG(&ops, b'p').unwrap_or("");
match arg.trim().parse::<i32>() { Ok(1) => printflags |= PRINT_LINE, Ok(0) => {} _ => {
crate::ported::utils::zwarnnam(name,
&format!("bad argument to -p: {}", arg)); crate::ported::mem::unqueue_signals();
return 1; }
}
}
}
if !hasargs { if !OPT_ISSET(&ops, b'm') { printflags &= !PRINT_WITH_NAMESPACE; }
if !OPT_ISSET(&ops, b'p') { if (on | roff) == 0 { printflags |= PRINT_TYPE; }
if roff != 0 || OPT_ISSET(&ops, b'+') { printflags |= PRINT_NAMEONLY; }
}
for (k, v) in std::env::vars() { if (printflags & PRINT_NAMEONLY) != 0 {
println!("{}", k);
} else {
println!("{}={}", k,
crate::ported::utils::quotedzputs(&v));
}
}
crate::ported::mem::unqueue_signals();
return 0; }
let nm0 = name.chars().next().unwrap_or(' ');
if nm0 == 'l' || OPT_PLUS(&ops, b'g') { on |= PM_LOCAL; } else if !OPT_ISSET(&ops, b'g') { if OPT_MINUS(&ops, b'x') { let globalexport = crate::ported::zsh_h::isset(crate::ported::options::optlookup("globalexport"));
let locallevel = LOCALLEVEL.load(std::sync::atomic::Ordering::Relaxed);
if globalexport { ops.ind[b'g' as usize] = 1; } else if locallevel != 0 { on |= PM_LOCAL; }
} else if !(OPT_ISSET(&ops, b'x') || OPT_ISSET(&ops, b'm')) { on |= PM_LOCAL; }
}
let _ = (off, returnval, name);
let is_hashed = (on & PM_HASHED) != 0; let is_array = (on & PM_ARRAY) != 0; for arg in argv {
let arg_name: &str = match arg.find('=') {
Some(i) => &arg[..i],
None => arg.as_str(),
};
if (on & PM_LOCAL) != 0
&& !arg_name.is_empty()
&& !arg_name.starts_with('-')
&& !arg_name.starts_with('+')
{
let kind = if is_hashed { PM_HASHED } else if is_array { PM_ARRAY } else { 0 };
let _ = crate::ported::params::createparam(
arg_name, on as i32 | kind as i32 | PM_LOCAL as i32);
}
if let Some(eq) = arg.find('=') {
let n = &arg[..eq];
let raw_v = &arg[eq + 1..];
let is_paren_init = raw_v.starts_with('(') && raw_v.ends_with(')')
&& raw_v.len() >= 2;
if is_paren_init {
let inner = &raw_v[1..raw_v.len()-1]; let elems: Vec<String> = inner.split_whitespace() .map(String::from)
.collect();
if is_hashed {
let bracket_shape = !elems.is_empty()
&& elems.iter().all(|e| {
e.starts_with('[')
&& e.contains("]=")
});
let mut map: indexmap::IndexMap<String, String>
= indexmap::IndexMap::new();
if bracket_shape {
for e in &elems {
let close = e.find("]=").unwrap();
let k = e[1..close].to_string();
let v = e[close + 2..].to_string();
map.insert(k, v);
}
} else {
let mut it = elems.into_iter(); while let Some(k) = it.next() {
let v = it.next().unwrap_or_default();
map.insert(k, v); }
}
let n_owned = n.to_string();
crate::fusevm_bridge::with_executor(|exec| {
exec.set_assoc(n_owned, map.clone());
});
} else {
let n_owned = n.to_string();
let elems_owned = elems.clone();
crate::fusevm_bridge::with_executor(|exec| {
exec.set_array(n_owned, elems_owned);
});
}
} else {
let lower = (on & PM_LOWER) != 0;
let upper = (on & PM_UPPER) != 0;
let folded: String = if lower {
raw_v.to_lowercase()
} else if upper {
raw_v.to_uppercase()
} else {
raw_v.to_string()
};
crate::ported::params::setsparam(n, &folded); let already_exported = std::env::var_os(n).is_some();
if (on & crate::ported::zsh_h::PM_EXPORTED) != 0 || already_exported {
std::env::set_var(n, &folded); }
let type_mask = (PM_INTEGER | PM_EFLOAT | PM_FFLOAT
| PM_LOWER | PM_UPPER | PM_READONLY) as i32;
let to_set = (on & (PM_INTEGER | PM_EFLOAT | PM_FFLOAT
| PM_LOWER | PM_UPPER | PM_READONLY)) as i32;
if to_set != 0 {
if let Ok(mut tab) = crate::ported::params::paramtab().write() {
if let Some(pm) = tab.get_mut(n) {
pm.node.flags = (pm.node.flags & !type_mask) | to_set;
}
}
}
}
} else if is_hashed || is_array {
let n_owned = arg.clone();
crate::fusevm_bridge::with_executor(|exec| {
if is_hashed {
if exec.assoc(&n_owned).is_none() {
exec.set_assoc(n_owned.clone(), indexmap::IndexMap::new());
}
} else if exec.array(&n_owned).is_none() {
exec.set_array(n_owned.clone(), Vec::new());
}
});
} else {
if crate::ported::params::getsparam(arg).is_none() {
crate::ported::params::setsparam(arg, ""); }
}
}
crate::ported::mem::unqueue_signals();
0
}
pub fn bin_whence(nam: &str, argv: &[String], ops: &crate::ported::zsh_h::options, func: i32) -> i32 {
let mut returnval: i32 = 0;
let mut printflags: i32 = 0;
let mut informed: i32 = 0;
let mut expand: i32 = 0;
let csh = OPT_ISSET(ops, b'c'); let v = OPT_ISSET(ops, b'v'); let all = OPT_ISSET(ops, b'a'); let wd = OPT_ISSET(ops, b'w');
if OPT_ISSET(ops, b'x') { let arg = OPT_ARG(ops, b'x').unwrap_or("");
match arg.trim().parse::<i32>() { Ok(n) => {
expand = n;
if expand == 0 { expand = -1; } }
Err(_) => {
crate::ported::utils::zwarnnam(nam, "number expected after -x"); return 1;
}
}
}
if OPT_ISSET(ops, b'w') { printflags |= PRINT_WHENCE_WORD; } else if OPT_ISSET(ops, b'c') { printflags |= PRINT_WHENCE_CSH; } else if OPT_ISSET(ops, b'v') { printflags |= PRINT_WHENCE_VERBOSE; } else { printflags |= PRINT_WHENCE_SIMPLE; } if OPT_ISSET(ops, b'f') { printflags |= PRINT_WHENCE_FUNCDEF; }
let mut v = v;
let _aliasflags = if func == BIN_COMMAND { if OPT_ISSET(ops, b'V') { printflags = PRINT_WHENCE_VERBOSE; v = true; PRINT_WHENCE_VERBOSE
} else {
printflags = PRINT_WHENCE_SIMPLE; PRINT_LIST }
} else {
printflags };
if OPT_ISSET(ops, b'm') { if all { if let Ok(mut m) = crate::ported::builtin::MATCHEDNODES.lock() {
m.clear();
}
}
crate::ported::mem::queue_signals(); for pat in argv { let pprog = crate::ported::pattern::patcompile(pat, crate::ported::zsh_h::PAT_HEAPDUP, None);
match pprog {
None => { crate::ported::utils::zwarnnam(nam,
&format!("bad pattern : {}", pat)); returnval = 1; continue;
}
Some(prog) => {
if !OPT_ISSET(ops, b'p') { if let Ok(t) = crate::ported::hashtable::aliastab_lock().read() {
for (n, _a) in t.iter() {
if crate::ported::pattern::pattry(&prog, n) {
println!("{}", n);
informed += 1; }
}
}
let reswords = ["do","done","esac","then","elif","else","fi",
"for","case","if","while","function","repeat",
"time","until","exec","command","select","coproc",
"nocorrect","foreach","end","!","[[","{","}",
"declare","export","float","integer","local",
"private","readonly","typeset"];
for w in &reswords { if crate::ported::pattern::pattry(&prog, w) {
println!("{}", w);
informed += 1; }
}
let names: Vec<String> = crate::ported::builtin::shfunctab_table()
.lock().map(|t| t.keys().cloned().collect())
.unwrap_or_default();
for n in &names {
if crate::ported::pattern::pattry(&prog, n) {
println!("{}", n);
informed += 1; }
}
for b in BUILTINS.iter() {
if crate::ported::pattern::pattry(&prog, &b.node.nam) {
println!("{}", b.node.nam);
informed += 1; }
}
}
if let Some(path) = crate::ported::params::getsparam("PATH") {
for dir in path.split(':') {
if dir.is_empty() { continue; }
if let Ok(rd) = std::fs::read_dir(dir) {
for entry in rd.flatten() {
if let Some(name) = entry.file_name().to_str() {
if crate::ported::pattern::pattry(&prog, name) {
if all {
if let Ok(mut m) =
crate::ported::builtin::MATCHEDNODES.lock() {
m.push(name.to_string());
}
} else {
println!("{}", name);
}
informed += 1; }
}
}
}
}
}
}
}
crate::ported::signals_h::run_queued_signals(); }
crate::ported::mem::unqueue_signals(); if !all { return if returnval != 0 || informed == 0 { 1 } else { 0 }; }
}
crate::ported::mem::queue_signals();
let argv_vec: Vec<String> = if OPT_ISSET(ops, b'm') {
crate::ported::builtin::MATCHEDNODES.lock()
.map(|m| m.clone()).unwrap_or_default()
} else { argv.to_vec() };
for arg in &argv_vec { informed = 0; let mut buf: Option<String> = None;
if !OPT_ISSET(ops, b'p') {
if let Ok(t) = crate::ported::hashtable::aliastab_lock().read() {
if let Some(a) = t.get(arg) { if (printflags & PRINT_WHENCE_WORD as i32) != 0 { println!("{}: alias", a.node.nam);
} else if (printflags & PRINT_WHENCE_CSH as i32) != 0 {
println!("{}: aliased to {}", a.node.nam, a.text);
} else if (printflags & PRINT_WHENCE_VERBOSE as i32) != 0 {
println!("{} is an alias for {}", a.node.nam, a.text);
} else if (printflags & PRINT_LIST as i32) != 0 {
println!("alias {}={}", a.node.nam, a.text);
} else {
println!("{}={}", a.node.nam, a.text);
}
informed = 1; if !all { continue; } }
}
if let Some(idx) = arg.rfind('.') { if idx > 0 && idx + 1 < arg.len() {
let suf = &arg[idx + 1..];
if let Ok(t) = crate::ported::hashtable::sufaliastab_lock().read() {
if let Some(a) = t.get(suf) { println!("{}={}", a.node.nam, a.text); informed = 1; if !all { continue; } }
}
}
}
let reswords = ["do","done","esac","then","elif","else","fi",
"for","case","if","while","function","repeat",
"time","until","exec","command","select","coproc",
"nocorrect","foreach","end","!","[[","{","}",
"declare","export","float","integer","local",
"private","readonly","typeset"];
if reswords.contains(&arg.as_str()) { if (printflags & PRINT_WHENCE_WORD as i32) != 0 {
println!("{}: reserved", arg);
} else if (printflags & PRINT_WHENCE_CSH as i32) != 0 {
println!("{}: shell reserved word", arg);
} else if (printflags & PRINT_WHENCE_VERBOSE as i32) != 0 {
println!("{} is a reserved word", arg);
} else {
println!("{}", arg); }
informed = 1; if !all { continue; } }
if let Ok(t) = crate::ported::builtin::shfunctab_table().lock() {
if t.contains_key(arg) { if (printflags & PRINT_WHENCE_FUNCDEF as i32) != 0 {
let body = crate::ported::utils::getshfunc(arg)
.unwrap_or_else(|| String::from("# body undefined"));
println!("{} () {{\n{}\n}}", arg, body);
} else if (printflags & PRINT_WHENCE_WORD as i32) != 0 {
println!("{}: function", arg);
} else if (printflags & PRINT_WHENCE_CSH as i32) != 0 {
println!("{}: shell function", arg);
} else if (printflags & PRINT_WHENCE_VERBOSE as i32) != 0 {
println!("{} is a shell function", arg);
} else {
println!("{}", arg); }
informed = 1; if !all { continue; } }
}
if BUILTINS.iter().any(|b| b.node.nam == *arg) { if wd {
println!("{}: builtin", arg); } else if csh {
println!("{}: shell built-in command", arg); } else if v {
println!("{} is a shell builtin", arg); } else {
println!("{}", arg); }
informed = 1; if !all { continue; } }
let hashed_path: Option<String> = {
match crate::ported::hashtable::cmdnamtab_lock().read() {
Ok(tab) => tab.get(arg).and_then(|cn| {
if (cn.node.flags & crate::ported::zsh_h::HASHED as i32) != 0 {
cn.cmd.clone() } else {
None
}
}),
Err(_) => None,
}
};
if let Some(p) = hashed_path {
if (printflags & PRINT_LIST) != 0 {
println!("hash {}={}", arg, p);
} else {
println!("{}", p);
}
informed = 1; if !all { continue; } }
}
if all && !arg.starts_with('/') { if let Some(path) = crate::ported::params::getsparam("PATH") {
for dir in path.split(':') {
if dir.is_empty() { continue; }
let full = format!("{}/{}", dir, arg);
let p = std::path::Path::new(&full);
if p.is_file() { if wd {
println!("{}: command", arg);
} else if v && !csh {
print!("{} is ", arg);
println!("{}", crate::ported::utils::quotedzputs(&full));
} else {
println!("{}", full);
}
informed = 1; }
}
}
if !informed != 0 && (wd || v || csh) { println!("{}{}", arg, if wd { ": none" } else { " not found" });
returnval = 1;
}
continue;
}
if func == BIN_COMMAND && OPT_ISSET(ops, b'p') { if BUILTINS.iter().any(|b| b.node.nam == *arg) { println!("{}: builtin", arg); informed = 1;
continue;
}
}
buf = findcmd(arg, 1, (func == BIN_COMMAND && OPT_ISSET(ops, b'p')) as i32);
if let Some(path) = buf { if wd { println!("{}: command", arg); } else if v && !csh { print!("{} is ", arg); println!("{}", crate::ported::utils::quotedzputs(&path)); } else {
println!("{}", path); }
informed = 1; continue;
}
if let Some(cnam) = findcmd(arg, 1, 0) { if wd { println!("{}: command", arg); } else if v && !csh { print!("{} is ", arg); println!("{}", crate::ported::utils::quotedzputs(&cnam)); } else {
println!("{}", cnam); }
informed = 1; continue;
}
if v || csh || wd { println!("{}{}", arg, if wd { ": none" } else { " not found" }); }
returnval = 1; }
crate::ported::mem::unqueue_signals();
returnval | (informed == 0) as i32 }
pub fn findcmd(name: &str, _docopy: i32, _default_path: i32) -> Option<String> { if name.contains('/') {
let p = std::path::Path::new(name);
return if p.is_file() { Some(name.to_string()) } else { None };
}
let path = crate::ported::params::getsparam("PATH")?;
for dir in path.split(':') {
if dir.is_empty() { continue; }
let candidate = format!("{}/{}", dir, name);
if std::path::Path::new(&candidate).is_file() {
return Some(candidate);
}
}
None
}
pub fn bin_ttyctl(_name: &str, _argv: &[String], ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
if OPT_ISSET(ops, b'f') { TTYFROZEN.store(1, Ordering::Relaxed); } else if OPT_ISSET(ops, b'u') { TTYFROZEN.store(0, Ordering::Relaxed); } else {
let f = TTYFROZEN.load(Ordering::Relaxed);
println!("tty is {}frozen", if f != 0 { "" } else { "not " }); }
0 }
pub static TTYFROZEN: std::sync::atomic::AtomicI32 =
std::sync::atomic::AtomicI32::new(0);
pub fn bin_break(name: &str, argv: &[String], _ops: &crate::ported::zsh_h::options, func: i32) -> i32 {
let mut num: i32 = LASTVAL.load(Ordering::Relaxed); let mut nump = 0i32; let implicit = argv.is_empty(); if !implicit { num = mathevali(&argv[0]).unwrap_or(0) as i32; nump = 1; }
if nump > 0 && (func == BIN_CONTINUE || func == BIN_BREAK) && num <= 0 { crate::ported::utils::zwarnnam(name, &format!("argument is not positive: {}", num)); return 1; }
let loops = LOOPS.load(Ordering::Relaxed);
match func {
x if x == BIN_CONTINUE => { if loops == 0 { crate::ported::utils::zwarnnam(name, "not in while, until, select, or repeat loop"); return 1; }
CONTFLAG.store(1, Ordering::Relaxed); if loops == 0 {
return 1;
}
BREAKS.store(if nump != 0 { num.min(loops) } else { 1 }, Ordering::Relaxed);
}
x if x == BIN_BREAK => { if loops == 0 { crate::ported::utils::zwarnnam(name, "not in while, until, select, or repeat loop"); return 1; }
BREAKS.store(if nump != 0 { num.min(loops) } else { 1 }, Ordering::Relaxed);
}
x if x == BIN_RETURN => {
let interactive = crate::ported::zsh_h::isset(crate::ported::options::optlookup("interactive"));
let shinstdin = crate::ported::zsh_h::isset(crate::ported::options::optlookup("shinstdin"));
let locallevel = LOCALLEVEL.load(Ordering::Relaxed);
let sourcelevel = SOURCELEVEL.load(Ordering::Relaxed);
if (interactive && shinstdin) || locallevel != 0 || sourcelevel != 0 { RETFLAG.store(1, Ordering::Relaxed); BREAKS.store(loops, Ordering::Relaxed); LASTVAL.store(num, Ordering::Relaxed); let posixtraps =
crate::ported::zsh_h::isset(crate::ported::options::optlookup("posixtraps"));
let cur_state =
crate::exec::TRAP_STATE.load(Ordering::Relaxed);
let cur_return =
crate::exec::TRAP_RETURN.load(Ordering::Relaxed);
if cur_state == crate::ported::zsh_h::TRAP_STATE_PRIMED && cur_return == -2 && !(posixtraps && implicit) {
crate::exec::TRAP_STATE.store( crate::ported::zsh_h::TRAP_STATE_FORCE_RETURN,
Ordering::Relaxed,
);
crate::exec::TRAP_RETURN.store(num, Ordering::Relaxed); }
return num; }
zexit(num, ZEXIT_NORMAL); }
x if x == BIN_LOGOUT => {
let loginshell = crate::ported::zsh_h::isset(crate::ported::options::optlookup("login"));
if !loginshell { crate::ported::utils::zwarnnam(name, "not login shell"); return 1; }
zexit(num, ZEXIT_NORMAL);
}
x if x == BIN_EXIT => {
zexit(num, ZEXIT_NORMAL);
}
_ => {}
}
0
}
pub static INEVAL: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static LOOPS: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static BREAKS: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static CONTFLAG: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static RETFLAG: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static LOCALLEVEL: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static SOURCELEVEL: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub const ZEXIT_NORMAL: i32 = 0;
pub fn bin_test(name: &str, argv: &[String], _ops: &crate::ported::zsh_h::options, func: i32) -> i32 {
let mut argv = argv.to_vec();
let mut sense = 0i32;
if func == BIN_BRACKET { if argv.is_empty() || argv.last().map(|s| s.as_str()) != Some("]") { crate::ported::utils::zwarnnam(name, "']' expected"); return 2; }
argv.pop(); }
if argv.is_empty() { return 1; }
let nargs = argv.len(); if nargs == 3 || nargs == 4 { if argv[0] == "(" && argv[nargs - 1] == ")" && (nargs != 3 || crate::ported::text::is_cond_binary_op(&argv[1]) == 0)
{
argv.pop(); argv.remove(0); }
}
if argv.len() == 3 && argv[0] == "!" { sense = 1; argv.remove(0); }
let args_refs: Vec<&str> = argv.iter().map(|s| s.as_str()).collect();
let options = std::collections::HashMap::new();
let mut variables = std::collections::HashMap::new();
for (k, v) in std::env::vars() {
variables.insert(k, v);
}
let posix = crate::ported::zsh_h::isset(crate::ported::options::optlookup("posixbuiltins"));
let mut ret = crate::ported::cond::evalcond(&args_refs, &options, &variables, posix);
if ret < 2 && sense != 0 { ret = if ret == 0 { 1 } else { 0 }; }
ret }
pub fn bin_unset(name: &str, argv: &[String], ops: &crate::ported::zsh_h::options, func: i32) -> i32 {
let mut returnval = 0i32; let mut match_count = 0i32;
#[cfg(feature = "recorder")]
if crate::recorder::is_enabled() {
let ctx = crate::recorder::recorder_ctx_global();
for a in argv {
if a.starts_with('-') || a == "--" { continue; }
crate::recorder::emit_unset(a, ctx.clone());
}
}
if OPT_ISSET(ops, b'f') { return bin_unhash(name, argv, ops, func); }
if OPT_ISSET(ops, b'm') { for s in argv { crate::ported::mem::queue_signals(); let pprog = crate::ported::pattern::patcompile(s, crate::ported::zsh_h::PAT_HEAPDUP, None);
if let Some(prog) = pprog {
let names: Vec<String> = std::env::vars()
.map(|(k,_)| k).collect();
for nm in &names {
if crate::ported::pattern::pattry(&prog, nm) { std::env::remove_var(nm); match_count += 1; }
}
} else {
crate::ported::utils::zwarnnam(name,
&format!("bad pattern : {}", s)); returnval = 1; }
crate::ported::mem::unqueue_signals(); }
if match_count == 0 { returnval = 1; }
return returnval; }
crate::ported::mem::queue_signals(); for s in argv { let (nm, subscript) = match s.find('[') { Some(start) if s.ends_with(']') => { (&s[..start], Some(&s[start + 1..s.len() - 1])) }
Some(_) => {
crate::ported::utils::zwarnnam(name,
&format!("{}: invalid parameter name", s)); returnval = 1; continue; }
None => (s.as_str(), None),
};
if nm.is_empty() || !nm.chars().next().map_or(false, |c| c.is_alphabetic() || c == '_')
|| !nm.chars().all(|c| c.is_alphanumeric() || c == '_')
{
crate::ported::utils::zwarnnam(name,
&format!("{}: invalid parameter name", s)); returnval = 1; continue;
}
match subscript { Some(key) => {
let nm_owned = nm.to_string();
let key_owned = key.to_string();
crate::fusevm_bridge::with_executor(|exec| {
if let Some(mut map) = exec.assoc(&nm_owned) {
map.shift_remove(&key_owned); exec.set_assoc(nm_owned.clone(), map);
} else if let Some(mut arr) = exec.array(&nm_owned) {
if let Ok(i) = key_owned.parse::<i32>() {
let idx = if i > 0 { (i - 1) as usize }
else { return; };
if idx < arr.len() {
arr[idx] = String::new();
exec.set_array(nm_owned.clone(), arr);
}
}
}
});
}
None => {
let nm_owned = nm.to_string();
crate::fusevm_bridge::with_executor(|exec| {
exec.unset_scalar(&nm_owned);
exec.unset_array(&nm_owned);
exec.unset_assoc(&nm_owned);
});
let _ = crate::ported::params::paramtab().write().ok().as_deref_mut()
.map(|t| t.remove(nm)); std::env::remove_var(nm); }
}
}
crate::ported::mem::unqueue_signals(); returnval }
pub fn bin_trap(name: &str, argv: &[String], _ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
#[cfg(feature = "recorder")]
if crate::recorder::is_enabled() {
let listing = argv.is_empty()
|| (argv.len() == 1 && (argv[0] == "-l" || argv[0] == "-p"));
if !listing && argv.len() >= 2 {
let ctx = crate::recorder::recorder_ctx_global();
let handler = &argv[0];
for sig in &argv[1..] {
crate::recorder::emit_trap(sig, handler, ctx.clone());
}
}
}
let mut argv = argv.to_vec();
if !argv.is_empty() && argv[0] == "--" { argv.remove(0); }
if argv.is_empty() { crate::ported::mem::queue_signals(); let traps = traps_table().lock().map(|t| t.clone()).unwrap_or_default();
for (sig, body) in traps.iter() { print!("trap -- "); print!("{}", crate::ported::utils::quotedzputs(body)); println!(" {}", sig); }
crate::ported::mem::unqueue_signals(); return 0; }
let first = &argv[0];
if getsigidx(first) != -1 || first == "-" { let start = if first == "-" { 1 } else { 0 }; if start >= argv.len() { if let Ok(mut t) = traps_table().lock() {
t.clear(); }
} else {
for arg in &argv[start..] { let sig = getsigidx(arg);
if sig == -1 { crate::ported::utils::zwarnnam(name,
&format!("undefined signal: {}", arg)); break; }
if let Ok(mut t) = traps_table().lock() {
t.remove(arg); }
}
}
return 0; }
let arg = argv.remove(0); if argv.is_empty() { if arg.starts_with("SIG") || arg.chars().next().is_some_and(|c| c.is_ascii_digit()) {
crate::ported::utils::zwarnnam(name,
&format!("undefined signal: {}", arg)); } else {
crate::ported::utils::zwarnnam(name, "signal expected"); }
return 1; }
for sigarg in &argv { let sig = getsigidx(sigarg);
if sig == -1 { crate::ported::utils::zwarnnam(name,
&format!("undefined signal: {}", sigarg)); break; }
if let Ok(mut t) = traps_table().lock() {
t.insert(sigarg.clone(), arg.clone()); }
}
0
}
static TRAPS_INNER: std::sync::OnceLock<std::sync::Mutex<std::collections::HashMap<String, String>>>
= std::sync::OnceLock::new();
pub fn traps_table() -> &'static std::sync::Mutex<std::collections::HashMap<String, String>> {
TRAPS_INNER.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()))
}
fn getsigidx(name: &str) -> i32 {
let s = name.strip_prefix("SIG").unwrap_or(name);
if let Ok(n) = s.parse::<i32>() {
return n;
}
match s {
"HUP" => 1, "INT" => 2, "QUIT" => 3, "ILL" => 4,
"TRAP" => 5, "ABRT" => 6, "FPE" => 8, "KILL" => 9,
"USR1" => 10, "SEGV" => 11, "USR2" => 12, "PIPE" => 13,
"ALRM" => 14, "TERM" => 15, "CHLD" => 17, "CONT" => 18,
"STOP" => 19, "TSTP" => 20, "TTIN" => 21, "TTOU" => 22,
"URG" => 23, "XCPU" => 24, "XFSZ" => 25, "VTALRM" => 26,
"PROF" => 27, "WINCH" => 28, "IO" => 29, "PWR" => 30,
"SYS" => 31, "EXIT" => 0,
_ => -1,
}
}
pub fn bin_enable(name: &str, argv: &[String], ops: &crate::ported::zsh_h::options, func: i32) -> i32 {
enum Tab { Builtin, Shfunc, Reswd, Alias, SufAlias }
let mut returnval = 0i32; let mut match_count = 0i32; if OPT_ISSET(ops, b'p') { return pat_enables(name, argv, func == BIN_ENABLE); }
let tab = if OPT_ISSET(ops, b'f') { Tab::Shfunc } else if OPT_ISSET(ops, b'r') { Tab::Reswd } else if OPT_ISSET(ops, b's') { Tab::SufAlias } else if OPT_ISSET(ops, b'a') { Tab::Alias } else { Tab::Builtin };
let enable = func == BIN_ENABLE;
let (flags1, flags2) = if enable { (0u32, DISABLED as u32) } else {
(DISABLED as u32, 0u32) };
let toggle_one = |tab: &Tab, nm: &str, on: bool| -> bool {
match tab {
Tab::Alias => crate::ported::hashtable::aliastab_lock().write()
.map(|mut t| if on { t.enable(nm) } else { t.disable(nm) })
.unwrap_or(false),
Tab::SufAlias => crate::ported::hashtable::sufaliastab_lock().write()
.map(|mut t| if on { t.enable(nm) } else { t.disable(nm) })
.unwrap_or(false),
Tab::Reswd => {
let exists = crate::ported::hashtable::reswdtab_lock().read()
.map(|t| t.get_including_disabled(nm).is_some())
.unwrap_or(false);
if !exists { return false; }
crate::ported::hashtable::reswdtab_lock().write()
.map(|mut t| if on { t.enable(nm) } else { t.disable(nm) })
.unwrap_or(false)
}
Tab::Shfunc => {
let exists = crate::ported::hashtable::shfunctab_lock().read()
.map(|t| t.get_including_disabled(nm).is_some())
.unwrap_or(false);
if !exists { return false; }
if on {
crate::ported::hashtable::enableshfuncnode(nm);
} else {
crate::ported::hashtable::disableshfuncnode(nm);
}
true
}
Tab::Builtin => {
if createbuiltintable().get(nm).is_none() { return false; }
if let Ok(mut set) = BUILTINS_DISABLED.lock() {
if on { set.remove(nm); } else { set.insert(nm.to_string()); }
return true;
}
false
}
}
};
let collect_names = |tab: &Tab| -> Vec<String> {
match tab {
Tab::Alias => crate::ported::hashtable::aliastab_lock().read()
.map(|t| t.iter().map(|(n,_)| n.clone()).collect()).unwrap_or_default(),
Tab::SufAlias => crate::ported::hashtable::sufaliastab_lock().read()
.map(|t| t.iter().map(|(n,_)| n.clone()).collect()).unwrap_or_default(),
Tab::Reswd => crate::ported::hashtable::reswdtab_lock().read()
.map(|t| t.iter().map(|(n,_)| n.clone()).collect()).unwrap_or_default(),
Tab::Shfunc => crate::ported::hashtable::shfunctab_lock().read()
.map(|t| t.iter().map(|(n,_)| n.clone()).collect()).unwrap_or_default(),
Tab::Builtin => createbuiltintable().keys().cloned().collect(),
}
};
if argv.is_empty() { crate::ported::mem::queue_signals(); for nm in collect_names(&tab) {
println!("{}", nm);
}
let _ = (flags1, flags2);
crate::ported::mem::unqueue_signals(); return 0; }
if OPT_ISSET(ops, b'm') { for arg in argv { crate::ported::mem::queue_signals(); let pprog = crate::ported::pattern::patcompile(arg, crate::ported::zsh_h::PAT_HEAPDUP, None);
if let Some(prog) = pprog {
for nm in collect_names(&tab) {
if crate::ported::pattern::pattry(&prog, &nm) { if toggle_one(&tab, &nm, enable) {
match_count += 1; }
}
}
} else {
crate::ported::utils::zwarnnam(name,
&format!("bad pattern : {}", arg)); returnval = 1; }
crate::ported::mem::unqueue_signals(); }
if match_count == 0 { returnval = 1; }
return returnval; }
crate::ported::mem::queue_signals(); for arg in argv { if !toggle_one(&tab, arg, enable) { crate::ported::utils::zwarnnam(name,
&format!("no such hash table element: {}", arg)); returnval = 1; }
}
crate::ported::mem::unqueue_signals(); returnval }
fn pat_enables(_name: &str, argv: &[String], _on: bool) -> i32 {
let _ = argv;
0
}
pub fn bin_hash(name: &str, argv: &[String], ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
let mut returnval = 0i32; let mut printflags = 0i32; let dir_mode = OPT_ISSET(ops, b'd');
#[cfg(feature = "recorder")]
if crate::recorder::is_enabled() && dir_mode {
let ctx = crate::recorder::recorder_ctx_global();
for a in argv {
if a.starts_with('-') { continue; }
if let Some((k, v)) = a.split_once('=') {
crate::recorder::emit_hash_d(k, v, ctx.clone());
}
}
}
if OPT_ISSET(ops, b'r') || OPT_ISSET(ops, b'f') { if !argv.is_empty() { crate::ported::utils::zwarnnam("hash", "too many arguments"); return 1; }
if OPT_ISSET(ops, b'r') { if dir_mode {
crate::ported::hashnameddir::emptynameddirtable();
} else {
crate::ported::hashtable::emptycmdnamtable();
}
}
if OPT_ISSET(ops, b'f') { if dir_mode {
crate::ported::hashnameddir::fillnameddirtable();
} else {
let path_str = crate::ported::params::getsparam("PATH").unwrap_or_default();
let path_arr: Vec<String> =
path_str.split(':').map(|s| s.to_string()).collect();
crate::ported::hashtable::fillcmdnamtable(&path_arr);
}
}
return 0; }
if OPT_ISSET(ops, b'L') { printflags |= PRINT_LIST; }
if argv.is_empty() { crate::ported::mem::queue_signals(); if dir_mode {
if let Ok(t) = crate::ported::hashnameddir::nameddirtab().lock() {
for (_n, nd) in t.iter() { crate::ported::hashnameddir::printnameddirnode(nd, printflags);
}
}
}
crate::ported::mem::unqueue_signals(); return 0; }
crate::ported::mem::queue_signals(); let mut idx = 0;
while idx < argv.len() { let arg = &argv[idx];
idx += 1;
if OPT_ISSET(ops, b'm') { let pprog = crate::ported::pattern::patcompile(arg, crate::ported::zsh_h::PAT_HEAPDUP, None);
if let Some(prog) = pprog {
if dir_mode {
if let Ok(t) = crate::ported::hashnameddir::nameddirtab().lock() {
for (n, nd) in t.iter() {
if crate::ported::pattern::pattry(&prog, n) { crate::ported::hashnameddir::printnameddirnode(nd, printflags);
}
}
}
}
} else {
crate::ported::utils::zwarnnam(name,
&format!("bad pattern : {}", arg)); returnval = 1; }
continue;
}
let (n, val) = match arg.find('=') {
Some(eq) => (&arg[..eq], Some(&arg[eq + 1..])),
None => (arg.as_str(), None),
};
if let Some(v) = val { if dir_mode { if !n.chars().all(|c| c.is_alphanumeric() || c == '_') { crate::ported::utils::zwarnnam(name,
&format!("invalid character in directory name: {}", n)); returnval = 1; continue; }
let nd = nameddir {
node: hashnode { next: None, nam: n.to_string(), flags: 0 },
dir: v.to_string(),
diff: 0,
};
crate::ported::hashnameddir::addnameddirnode(n, nd); } else {
std::env::set_var(format!("__zshrs_hash_{}", n), v);
}
if OPT_ISSET(ops, b'v') { if dir_mode {
if let Ok(t) = crate::ported::hashnameddir::nameddirtab().lock() {
if let Some(nd) = t.get(n) { crate::ported::hashnameddir::printnameddirnode(nd, 0);
}
}
}
}
} else {
if dir_mode {
let snapshot = crate::ported::hashnameddir::nameddirtab()
.lock().ok().and_then(|t| t.get(n).cloned());
match snapshot {
Some(nd) => {
if OPT_ISSET(ops, b'v') { crate::ported::hashnameddir::printnameddirnode(&nd, 0);
}
}
None => {
crate::ported::utils::zwarnnam(name,
&format!("no such directory name: {}", n)); returnval = 1; }
}
} else {
let found = crate::ported::params::getsparam("PATH").is_some_and(|p| {
p.split(':').any(|d|
!d.is_empty() && std::path::Path::new(&format!("{}/{}", d, n)).exists()
)
});
if !found {
crate::ported::utils::zwarnnam(name,
&format!("no such command: {}", n)); returnval = 1; }
}
}
}
crate::ported::mem::unqueue_signals(); returnval }
pub fn bin_unhash(name: &str, argv: &[String], ops: &crate::ported::zsh_h::options, func: i32) -> i32 {
let mut returnval = 0i32; let mut all = 0i32; let mut match_count = 0i32;
#[cfg(feature = "recorder")]
if crate::recorder::is_enabled() && func == crate::ported::builtin::BIN_UNALIAS {
let ctx = crate::recorder::recorder_ctx_global();
for a in argv {
if a.starts_with('-') && a != "-" { continue; }
crate::recorder::emit_unalias(a, ctx.clone());
}
}
enum Tab { CmdNam, NamedDir, Shfunc, Alias, SufAlias }
let tab: Tab;
if func == BIN_UNALIAS { tab = if OPT_ISSET(ops, b's') { Tab::SufAlias } else { Tab::Alias }; if OPT_ISSET(ops, b'a') { if !argv.is_empty() { crate::ported::utils::zwarnnam(name, "-a: too many arguments"); return 1; }
all = 1; } else if argv.is_empty() { crate::ported::utils::zwarnnam(name, "not enough arguments"); return 1; }
} else if OPT_ISSET(ops, b'd') { tab = Tab::NamedDir; } else if OPT_ISSET(ops, b'f') { tab = Tab::Shfunc; } else if OPT_ISSET(ops, b's') { tab = Tab::SufAlias; } else if func == BIN_UNHASH && OPT_ISSET(ops, b'a') { tab = Tab::Alias; } else { tab = Tab::CmdNam; }
let clear_all = |t: &Tab| match t {
Tab::Alias => { let _ = crate::ported::hashtable::aliastab_lock().write().map(|mut g| g.clear()); }
Tab::SufAlias => { let _ = crate::ported::hashtable::sufaliastab_lock().write().map(|mut g| g.clear()); }
Tab::NamedDir => { crate::ported::hashnameddir::emptynameddirtable(); }
Tab::Shfunc => { let _ = shfunctab_table().lock().map(|mut g| g.clear()); }
Tab::CmdNam => { crate::ported::hashtable::emptycmdnamtable(); } };
let remove_one = |t: &Tab, nm: &str| -> bool {
match t {
Tab::Alias => crate::ported::hashtable::aliastab_lock().write()
.map(|mut g| g.remove(nm).is_some()).unwrap_or(false),
Tab::SufAlias => crate::ported::hashtable::sufaliastab_lock().write()
.map(|mut g| g.remove(nm).is_some()).unwrap_or(false),
Tab::NamedDir => crate::ported::hashnameddir::removenameddirnode(nm).is_some(),
Tab::Shfunc => shfunctab_table().lock()
.map(|mut g| g.remove(nm).is_some()).unwrap_or(false),
Tab::CmdNam => {
crate::ported::hashtable::freecmdnamnode(nm);
true
}
}
};
if all != 0 { crate::ported::mem::queue_signals(); clear_all(&tab); crate::ported::mem::unqueue_signals(); return 0; }
if OPT_ISSET(ops, b'm') { for arg in argv { crate::ported::mem::queue_signals(); let pprog = crate::ported::pattern::patcompile(arg, crate::ported::zsh_h::PAT_HEAPDUP, None);
if let Some(prog) = pprog {
let names: Vec<String> = match &tab {
Tab::Alias => crate::ported::hashtable::aliastab_lock().read()
.map(|t| t.iter().map(|(n,_)| n.clone()).collect()).unwrap_or_default(),
Tab::SufAlias => crate::ported::hashtable::sufaliastab_lock().read()
.map(|t| t.iter().map(|(n,_)| n.clone()).collect()).unwrap_or_default(),
Tab::NamedDir => crate::ported::hashnameddir::nameddirtab().lock()
.map(|t| t.keys().cloned().collect()).unwrap_or_default(),
Tab::Shfunc => shfunctab_table().lock()
.map(|t| t.keys().cloned().collect()).unwrap_or_default(),
Tab::CmdNam => Vec::new(),
};
for nm in &names {
if crate::ported::pattern::pattry(&prog, nm) { if remove_one(&tab, nm) {
match_count += 1; }
}
}
} else {
crate::ported::utils::zwarnnam(name,
&format!("bad pattern : {}", arg)); returnval = 1; }
crate::ported::mem::unqueue_signals(); }
if match_count == 0 { returnval = 1; }
return returnval; }
crate::ported::mem::queue_signals(); for arg in argv { if remove_one(&tab, arg) { } else if func == BIN_UNSET
&& crate::ported::zsh_h::isset(crate::ported::options::optlookup("posixbuiltins"))
{
returnval = 0; } else {
crate::ported::utils::zwarnnam(name,
&format!("no such hash table element: {}", arg)); returnval = 1; }
}
crate::ported::mem::unqueue_signals(); returnval }
pub fn bin_alias(name: &str, argv: &[String], ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
let mut returnval = 0i32; let mut flags1 = 0u32; let mut flags2 = DISABLED as u32; let mut printflags = 0i32; let mut use_suffix = false;
let type_opts = (OPT_ISSET(ops, b'r') as i32) + (OPT_ISSET(ops, b'g') as i32)
+ (OPT_ISSET(ops, b's') as i32);
if type_opts != 0 { if type_opts > 1 { crate::ported::utils::zwarnnam(name, "illegal combination of options"); return 1; }
if OPT_ISSET(ops, b'g') { flags1 |= ALIAS_GLOBAL as u32; } else {
flags2 |= ALIAS_GLOBAL as u32; }
if OPT_ISSET(ops, b's') { flags1 |= ALIAS_SUFFIX as u32; use_suffix = true; } else {
flags2 |= ALIAS_SUFFIX as u32; }
}
if OPT_ISSET(ops, b'L') { printflags |= PRINT_LIST; } else if OPT_PLUS(ops, b'g') || OPT_PLUS(ops, b'r') || OPT_PLUS(ops, b's')
|| OPT_PLUS(ops, b'm') || OPT_ISSET(ops, b'+') {
printflags |= PRINT_NAMEONLY; }
let print_alias = |a: &Alias, pflags: i32| {
if (pflags & PRINT_NAMEONLY) != 0 {
println!("{}", a.node.nam);
} else if (pflags & PRINT_LIST) != 0 {
println!("alias {}={}", a.node.nam, a.text);
} else {
println!("{}={}", a.node.nam, a.text);
}
};
if argv.is_empty() { crate::ported::mem::queue_signals(); let lock = if use_suffix { sufaliastab_lock() } else { aliastab_lock() };
if let Ok(t) = lock.read() {
for (_n, a) in t.iter() { if (a.node.flags & flags1 as i32) == flags1 as i32
&& (a.node.flags & flags2 as i32) == 0 {
print_alias(a, printflags);
}
}
}
crate::ported::mem::unqueue_signals(); return 0; }
if OPT_ISSET(ops, b'm') { for pat in argv { crate::ported::mem::queue_signals(); let pprog = crate::ported::pattern::patcompile(pat, crate::ported::zsh_h::PAT_HEAPDUP, None);
if let Some(prog) = pprog {
let lock = if use_suffix { sufaliastab_lock() } else { aliastab_lock() };
if let Ok(t) = lock.read() {
for (_n, a) in t.iter() { if (a.node.flags & flags1 as i32) == flags1 as i32
&& (a.node.flags & flags2 as i32) == 0
&& crate::ported::pattern::pattry(&prog, &a.node.nam)
{
print_alias(a, printflags);
}
}
}
} else {
crate::ported::utils::zwarnnam(name,
&format!("bad pattern : {}", pat)); returnval = 1; }
crate::ported::mem::unqueue_signals(); }
return returnval; }
crate::ported::mem::queue_signals(); let mut idx = 0;
while idx < argv.len() { let arg = &argv[idx];
idx += 1;
if let Some(eq) = arg.find('=') { if !OPT_ISSET(ops, b'L') { let n = &arg[..eq];
let v = &arg[eq + 1..];
let lock = if use_suffix { sufaliastab_lock() } else { aliastab_lock() };
if let Ok(mut t) = lock.write() {
let a = crate::ported::hashtable::createaliasnode(n, v, flags1); t.add(a);
}
continue;
}
}
let n = if let Some(eq) = arg.find('=') { &arg[..eq] } else { arg.as_str() };
let lock = if use_suffix { sufaliastab_lock() } else { aliastab_lock() };
let found = lock.read().ok().and_then(|t|
t.get_including_disabled(n).map(|a| (a.node.nam.clone(), a.node.flags as u32, a.text.clone()))
);
match found {
Some((nm, fl, txt)) => { let show = type_opts == 0
|| use_suffix
|| (OPT_ISSET(ops, b'r')
&& (fl & (ALIAS_GLOBAL | ALIAS_SUFFIX) as u32) == 0)
|| (OPT_ISSET(ops, b'g')
&& (fl & ALIAS_GLOBAL as u32) != 0);
if show {
let a = crate::ported::hashtable::createaliasnode(&nm, &txt, fl);
print_alias(&a, printflags);
}
}
None => { returnval = 1; }
}
}
crate::ported::mem::unqueue_signals(); returnval }
pub fn bin_umask(nam: &str, args: &[String], ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
crate::ported::mem::queue_signals(); let mut um: u32 = unsafe { libc::umask(0o777) } as u32; unsafe { libc::umask(um as libc::mode_t); } crate::ported::mem::unqueue_signals();
if args.is_empty() { if OPT_ISSET(ops, b'S') { let who_chars = ['u', 'g', 'o']; for (i, who) in who_chars.iter().enumerate() { print!("{}=", who); let mut what_iter = ['r', 'w', 'x'].iter(); while let Some(w) = what_iter.next() { if (um & 0o400) == 0 { print!("{}", w); }
um <<= 1; }
if i < 2 { print!(","); } else { println!(); } }
} else {
if (um & 0o700) != 0 { print!("0"); }
println!("{:03o}", um); }
return 0; }
let s = &args[0];
if s.chars().next().is_some_and(|c| c.is_ascii_digit()) { match u32::from_str_radix(s, 8) { Ok(n) => um = n, Err(_) => {
crate::ported::utils::zwarnnam(nam, "bad umask"); return 1; }
}
} else {
let bytes = s.as_bytes();
let mut i = 0;
loop {
let mut whomask: u32 = 0; while i < bytes.len() { match bytes[i] {
b'u' => { whomask |= 0o700; i += 1; } b'g' => { whomask |= 0o070; i += 1; } b'o' => { whomask |= 0o007; i += 1; } b'a' => { whomask |= 0o777; i += 1; } _ => break,
}
}
if whomask == 0 { whomask = 0o777; } let umaskop = if i < bytes.len() { bytes[i] } else { 0 }; if !(umaskop == b'+' || umaskop == b'-' || umaskop == b'=') { if umaskop != 0 { crate::ported::utils::zwarnnam(nam,
&format!("bad symbolic mode operator: {}", umaskop as char)); } else {
crate::ported::utils::zwarnnam(nam, "bad umask"); }
return 1; }
i += 1;
let mut mask: u32 = 0; while i < bytes.len() && bytes[i] != b',' { match bytes[i] {
b'r' => mask |= 0o444 & whomask, b'w' => mask |= 0o222 & whomask, b'x' => mask |= 0o111 & whomask, other => {
crate::ported::utils::zwarnnam(nam,
&format!("bad symbolic mode permission: {}", other as char)); return 1; }
}
i += 1;
}
match umaskop {
b'+' => um &= !mask, b'-' => um |= mask, _ => um = (um | whomask) & !mask, }
if i < bytes.len() && bytes[i] == b',' { i += 1; } else {
break; }
}
if i < bytes.len() { crate::ported::utils::zwarnnam(nam,
&format!("bad character in symbolic mode: {}", bytes[i] as char)); return 1; }
}
unsafe { libc::umask(um as libc::mode_t); } 0 }
pub fn bin_emulate(nam: &str, argv: &[String], ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
let opt_l = OPT_ISSET(ops, b'l'); let opt_l_arg = OPT_ISSET(ops, b'L'); let opt_r = OPT_ISSET(ops, b'R');
if argv.is_empty() { if opt_l_arg || opt_r { crate::ported::utils::zwarnnam(nam, "not enough arguments"); return 1; }
let bits = crate::ported::options::emulation
.load(std::sync::atomic::Ordering::Relaxed) as i32;
let shname = if (bits & EMULATE_CSH) != 0 { "csh" } else if (bits & EMULATE_KSH) != 0 { "ksh" } else if (bits & EMULATE_SH) != 0 { "sh" } else { "zsh" }; println!("{}", shname); return 0; }
let shname = &argv[0];
if argv.len() == 1 { let bits = match shname.as_str() {
"csh" => EMULATE_CSH,
"ksh" => EMULATE_KSH,
"sh" => EMULATE_SH,
_ => crate::ported::zsh_h::EMULATE_ZSH,
};
crate::ported::options::emulation
.store(bits, std::sync::atomic::Ordering::Relaxed);
let mut cmdopts: std::collections::HashMap<String, bool> =
std::collections::HashMap::new();
for n in crate::ported::options::ZSH_OPTIONS_SET.iter() {
cmdopts.insert(
n.to_string(),
crate::ported::options::opt_state_get(n).unwrap_or(false),
);
}
if !opt_l {
let mode = shname.as_str();
let _ = mode;
}
if opt_l_arg { for nm in ["localoptions", "localtraps", "localpatterns"] {
cmdopts.insert(nm.to_string(), true);
if !opt_l {
crate::ported::options::opt_state_set(nm, true);
}
}
}
if opt_l { crate::ported::options::list_emulate_options(&cmdopts, opt_r);
return 0; }
crate::ported::pattern::clearpatterndisables();
return 0; }
if opt_l { crate::ported::utils::zwarnnam(nam, "too many arguments for -l"); return 1; }
let _ = (opt_r, shname);
0
}
pub fn bin_dirs(_name: &str, argv: &[String], ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
crate::ported::mem::queue_signals(); if (argv.is_empty() && !OPT_ISSET(ops, b'c')) || OPT_ISSET(ops, b'v')
|| OPT_ISSET(ops, b'p')
{
let mut pos = 1; let fmt: &str = if OPT_ISSET(ops, b'v') { print!("0\t"); "\n{}\t" } else if OPT_ISSET(ops, b'p') { "\n"
} else {
" "
};
let pwd = crate::ported::params::getsparam("PWD")
.unwrap_or_else(|| crate::ported::utils::zgetcwd().unwrap_or_default());
if OPT_ISSET(ops, b'l') { print!("{}", pwd); } else {
let home = crate::ported::params::getsparam("HOME").unwrap_or_default();
if !home.is_empty() && pwd.starts_with(&home) {
print!("~{}", &pwd[home.len()..]); } else {
print!("{}", pwd);
}
}
if let Ok(stack) = DIRSTACK.lock() { for entry in stack.iter() {
if fmt == "\n{}\t" {
print!("\n{}\t", pos);
} else {
print!("{}", fmt); }
pos += 1; if OPT_ISSET(ops, b'l') { print!("{}", entry); } else {
let home = crate::ported::params::getsparam("HOME").unwrap_or_default();
if !home.is_empty() && entry.starts_with(&home) {
print!("~{}", &entry[home.len()..]);
} else {
print!("{}", entry);
}
}
}
}
crate::ported::mem::unqueue_signals(); println!(); return 0; }
if let Ok(mut stack) = DIRSTACK.lock() {
stack.clear(); for arg in argv {
stack.push(arg.clone()); }
}
crate::ported::mem::unqueue_signals(); 0 }
pub fn bin_dot(name: &str, argv: &[String], _ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
if argv.is_empty() { return 0; }
#[cfg(feature = "recorder")]
if crate::recorder::is_enabled() && !argv[0].is_empty() {
let ctx = crate::recorder::recorder_ctx_global();
crate::recorder::emit_source(&argv[0], ctx);
}
let saved_pparams: Option<Vec<String>> = if argv.len() > 1 { let mut pp = PPARAMS.lock().unwrap_or_else(|e| { PPARAMS.clear_poison(); e.into_inner() });
let saved = pp.clone();
*pp = argv[1..].to_vec(); Some(saved)
} else { None };
let arg0 = argv[0].clone(); let _enam = arg0.clone(); let saved_argzero: Option<Option<String>> =
if isset(crate::ported::zsh_h::FUNCTIONARGZERO) {
let prev = crate::ported::utils::argzero();
crate::ported::utils::set_argzero(Some(arg0.clone()));
Some(prev)
} else {
None
};
let mut diddot = 0i32; let mut dotdot = 0i32;
let mut found_path: Option<String> = None;
if !name.starts_with('.') { let p = std::path::Path::new(&arg0);
if p.exists() && !p.is_dir() { diddot = 1; found_path = Some(arg0.clone()); }
}
if found_path.is_none() && arg0.contains('/') { if arg0.starts_with("./") { diddot += 1; } else if arg0.starts_with("../") { dotdot += 1; } let p = std::path::Path::new(&arg0);
if p.exists() && !p.is_dir() {
found_path = Some(arg0.clone()); }
}
let pathdirs = crate::ported::zsh_h::isset(crate::ported::options::optlookup("pathdirs"));
if found_path.is_none() && (!arg0.contains('/') || (pathdirs && diddot < 2 && dotdot == 0)) { let path_env = crate::ported::params::getsparam("PATH").unwrap_or_default();
for dir in path_env.split(':') { let buf = if dir.is_empty() || dir == "." { if diddot != 0 { continue; }
diddot = 1; arg0.clone() } else {
format!("{}/{}", dir, arg0) };
let p = std::path::Path::new(&buf);
if p.exists() && !p.is_dir() { found_path = Some(buf); break;
}
}
}
if let Some(saved) = saved_pparams { let mut pp = PPARAMS.lock().unwrap_or_else(|e| { PPARAMS.clear_poison(); e.into_inner() });
*pp = saved; }
if let Some(prev) = saved_argzero.clone() {
crate::ported::utils::set_argzero(prev);
}
let path = match found_path {
Some(p) => p,
None => { let posix = crate::ported::zsh_h::isset(crate::ported::options::optlookup("posixbuiltins"));
let msg = format!("{}: {}", "no such file or directory", arg0); if posix {
crate::ported::utils::zwarnnam(name, &msg); } else {
crate::ported::utils::zwarnnam(name, &msg); }
return 1;
}
};
let result = match std::fs::read_to_string(&path) { Ok(_src) => {
let _ = path;
0
}
Err(_) => 1,
};
if let Some(prev) = saved_argzero {
crate::ported::utils::set_argzero(prev);
}
result
}
pub fn bin_set(nam: &str, args: &[String], _ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
#[cfg(feature = "recorder")]
if crate::recorder::is_enabled() && !args.is_empty() {
let ctx = crate::recorder::recorder_ctx_global();
let mut iter = args.iter().peekable();
while let Some(a) = iter.next() {
match a.as_str() {
"-o" => {
if let Some(name) = iter.next() {
crate::recorder::emit_setopt(name, ctx.clone());
}
}
"+o" => {
if let Some(name) = iter.next() {
crate::recorder::emit_unsetopt(name, ctx.clone());
}
}
_ => {}
}
}
}
let mut argv: Vec<String> = args.to_vec();
let mut hadopt = false; let mut hadplus = false; let mut hadend = false; let mut sort: i32 = 0; let mut array: i32 = 0; let mut arrayname: Option<String> = None;
if !EMULATION(EMULATE_ZSH) && !argv.is_empty() && argv[0] == "-"
{
let v = crate::ported::options::optlookup("verbose");
let x = crate::ported::options::optlookup("xtrace");
crate::ported::options::dosetopt(v, 0, 0); crate::ported::options::dosetopt(x, 0, 0); if argv.len() == 1 { return 0; } argv.remove(0);
}
let mut idx = 0usize;
'outer: while idx < argv.len() && (argv[idx].starts_with('-') || argv[idx].starts_with('+'))
{
let arg = argv[idx].clone();
let action = arg.starts_with('-'); if !action { hadplus = true; } let body: String = if arg.len() == 1 { "--".to_string() }
else { arg.clone() };
let chars: Vec<char> = body[1..].chars().collect();
let mut ci = 0usize;
while ci < chars.len() { let c = chars[ci];
if c != '-' || action { hadopt = true; } if c == '-' { hadend = true; idx += 1; break 'outer;
}
if c == 'o' { let optname: String = if ci + 1 < chars.len() {
chars[ci + 1..].iter().collect::<String>()
} else {
idx += 1;
if idx >= argv.len() { return 0;
}
argv[idx].clone()
};
let optno = crate::ported::options::optlookup(&optname); if optno == 0 { crate::ported::utils::zerr(&format!(
"no such option: {}", optname)); } else if crate::ported::options::dosetopt(optno,
if action { 1 } else { 0 }, 0) != 0 {
crate::ported::utils::zerr(&format!(
"can't change option: {}", optname)); }
break;
}
if c == 'A' { array = if action { 1 } else { -1 }; let nameopt: Option<String> = if ci + 1 < chars.len() {
Some(chars[ci + 1..].iter().collect::<String>())
} else if idx + 1 < argv.len() {
idx += 1;
Some(argv[idx].clone())
} else { None };
arrayname = nameopt.clone();
if arrayname.is_none() { idx += 1;
break 'outer;
}
let ksharrays = crate::ported::zsh_h::isset(crate::ported::options::optlookup("ksharrays"));
if !ksharrays { idx += 1; break 'outer; }
break;
}
if c == 's' { sort = if action { 1 } else { -1 }; } else {
let optno = crate::ported::options::optlookupc(c); if optno == 0 { crate::ported::utils::zerr(&format!("bad option: -{}", c)); } else if crate::ported::options::dosetopt(optno,
if action { 1 } else { 0 }, 0) != 0 {
crate::ported::utils::zerr(&format!("can't change option: -{}", c)); }
}
ci += 1;
}
idx += 1; }
let _ = nam;
crate::ported::mem::queue_signals();
let remaining = &argv[idx..];
if arrayname.is_none() { if !hadopt && remaining.is_empty() { for (k, v) in std::env::vars() {
if hadplus { println!("{}", k);
} else {
println!("{}={}", k,
crate::ported::utils::quotedzputs(&v));
}
}
}
if array != 0 { }
if remaining.is_empty() && !hadend { crate::ported::mem::unqueue_signals();
return 0; }
}
let sorted: Vec<String> = if sort != 0 {
let mut v = remaining.to_vec();
if sort < 0 { v.sort_by(|a, b| b.cmp(a)); } else { v.sort(); }
v
} else {
remaining.to_vec()
};
if array != 0 { let aname = arrayname.unwrap_or_default();
let mut new_arr: Vec<String> = sorted;
if array < 0 { let existing: Vec<String> = {
let tab = crate::ported::params::paramtab().read().unwrap();
tab.get(&aname).and_then(|pm| pm.u_arr.clone()).unwrap_or_default()
};
if existing.len() > new_arr.len() { new_arr.extend(existing.into_iter().skip(new_arr.len())); }
}
crate::ported::params::setaparam(&aname, new_arr);
} else {
if let Ok(mut pp) = PPARAMS.lock() {
*pp = sorted; }
}
crate::ported::mem::unqueue_signals(); 0 }