#![allow(non_camel_case_types, non_snake_case)]
use crate::ported::utils::zwarnnam;
use std::sync::{Mutex, OnceLock};
use std::io::Read;
use crate::ported::zsh_h::{OPT_ISSET, OPT_ARG};
use std::os::unix::fs::DirBuilderExt;
use std::os::unix::fs::PermissionsExt;
pub const BIN_LN: i32 = 0; pub const BIN_MV: i32 = 1;
pub const MV_NODIRS: i32 = 1 << 0; pub const MV_FORCE: i32 = 1 << 1; pub const MV_INTERACTIVE: i32 = 1 << 2; pub const MV_ASKNW: i32 = 1 << 3; pub const MV_ATOMIC: i32 = 1 << 4; pub const MV_NOCHASETARGET: i32 = 1 << 5;
pub const BIN_CHOWN: i32 = 0; pub const BIN_CHGRP: i32 = 1;
pub fn ask() -> i32 { let mut buf = [0u8; 1];
let stdin = std::io::stdin();
let mut handle = stdin.lock();
let a = match handle.read(&mut buf) { Ok(0) => return 0,
Ok(_) => buf[0],
Err(_) => return 0,
};
while a != b'\n' { let mut peek = [0u8; 1];
match handle.read(&mut peek) {
Ok(0) => break,
Ok(_) => if peek[0] == b'\n' { break },
Err(_) => break,
}
}
if a == b'y' || a == b'Y' { 1 } else { 0 } }
pub fn bin_sync(_nam: &str, _args: &[String], _ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
unsafe { libc::sync(); } 0 }
pub fn bin_mkdir(nam: &str, args: &[String], ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
let oumask = unsafe { libc::umask(0) }; let mut mode: u32 = 0o777 & !(oumask as u32); let mut err = 0i32;
unsafe { libc::umask(oumask); } if OPT_ISSET(ops, b'm') { let str_arg = OPT_ARG(ops, b'm').unwrap_or(""); match i64::from_str_radix(str_arg, 8) { Ok(m) => mode = m as u32,
Err(_) => {
zwarnnam(nam, &format!("invalid mode `{}'", str_arg));
return 1; }
}
}
let p_flag = if OPT_ISSET(ops, b'p') { 1 } else { 0 }; for arg in args { let trimmed: String = if arg.starts_with('/') { let body = arg.trim_end_matches('/');
if body.is_empty() { "/".to_string() } else { body.to_string() }
} else {
arg.trim_end_matches('/').to_string()
};
if p_flag != 0 { let bytes = trimmed.as_bytes();
let mut i = 0usize;
loop {
while i < bytes.len() && bytes[i] == b'/' { i += 1; } while i < bytes.len() && bytes[i] != b'/' { i += 1; } if i >= bytes.len() { err |= domkdir(nam, &trimmed, mode, 1); break;
}
let prefix = &trimmed[..i]; let e = domkdir(nam, prefix, mode | 0o300, 1); if e != 0 { err = 1; break; }
}
} else {
err |= domkdir(nam, &trimmed, mode, 0); }
}
err }
pub fn domkdir(nam: &str, path: &str, mode: u32, p: i32) -> i32 { let mut n = 8; let mut last_err: i32 = 0;
while n > 0 { n -= 1;
let oumask = unsafe { libc::umask(0) }; let mut builder = std::fs::DirBuilder::new();
builder.mode(mode);
let result = builder.create(path); unsafe { libc::umask(oumask); } match result {
Ok(()) => return 0, Err(e) => last_err = e.raw_os_error().unwrap_or(0),
}
if p == 0 || last_err != libc::EEXIST { break; } match std::fs::metadata(path) { Ok(meta) if meta.is_dir() => return 0, Ok(_) => break, Err(e) => {
last_err = e.raw_os_error().unwrap_or(0);
if last_err == libc::ENOENT { continue; } break; }
}
}
zwarnnam(nam, &format!("cannot make directory `{}': {}",
path, std::io::Error::from_raw_os_error(last_err)));
1 }
pub fn bin_rmdir(nam: &str, args: &[String], _ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
let mut err = 0i32;
for arg in args { let cpath = match std::ffi::CString::new(arg.as_str()) { Ok(c) => c,
Err(_) => {
zwarnnam(nam, &format!("{}: {}", arg, "name too long"));
err = 1;
continue;
}
};
let r = unsafe { libc::rmdir(cpath.as_ptr()) }; if r != 0 { zwarnnam(nam, &format!("cannot remove directory `{}': {}",
arg, std::io::Error::last_os_error()));
err = 1; }
}
err }
#[allow(non_camel_case_types)]
pub type MoveFunc = fn(p: &std::ffi::CStr, q: &std::ffi::CStr) -> i32;
pub fn mv_rename(p: &std::ffi::CStr, q: &std::ffi::CStr) -> i32 {
unsafe { libc::rename(p.as_ptr(), q.as_ptr()) }
}
pub fn mv_symlink(p: &std::ffi::CStr, q: &std::ffi::CStr) -> i32 {
unsafe { libc::symlink(p.as_ptr(), q.as_ptr()) }
}
pub fn mv_link(p: &std::ffi::CStr, q: &std::ffi::CStr) -> i32 {
unsafe { libc::link(p.as_ptr(), q.as_ptr()) }
}
pub fn bin_ln(nam: &str, args: &[String], ops: &crate::ported::zsh_h::options, func: i32) -> i32 {
let movefn: MoveFunc;
let mut flags: i32;
let mut err = 0i32;
if func == BIN_MV { movefn = mv_rename; flags = if OPT_ISSET(ops, b'f') { 0 } else { MV_ASKNW }; flags |= MV_ATOMIC; } else {
flags = if OPT_ISSET(ops, b'f') { MV_FORCE } else { 0 }; if OPT_ISSET(ops, b'h') || OPT_ISSET(ops, b'n') { flags |= MV_NOCHASETARGET;
}
if OPT_ISSET(ops, b's') { movefn = mv_symlink; } else {
movefn = mv_link; if !OPT_ISSET(ops, b'd') { flags |= MV_NODIRS;
}
}
}
if OPT_ISSET(ops, b'i') && !OPT_ISSET(ops, b'f') { flags |= MV_INTERACTIVE;
}
if args.is_empty() {
zwarnnam(nam, "missing file argument");
return 1;
}
let last_idx = args.len() - 1; let mut have_dir = false;
if last_idx > 0 { let target = &args[last_idx];
if let Ok(meta) = std::fs::metadata(target) { if meta.is_dir() { have_dir = true;
if (flags & MV_NOCHASETARGET) != 0 { if let Ok(lmeta) = std::fs::symlink_metadata(target) {
if lmeta.file_type().is_symlink() { if last_idx > 1 { zwarnnam(nam, &format!("{}: not a directory", target));
return 1; }
if (flags & MV_FORCE) != 0 { let _ = std::fs::remove_file(target); have_dir = false; } else {
zwarnnam(nam, &format!("{}: file exists", target));
return 1; }
}
}
}
}
}
}
if have_dir { let dir = args[last_idx].trim_end_matches('/').to_string();
for src in &args[..last_idx] { let basename = match src.rsplit_once('/') { Some((_, n)) => n,
None => src.as_str(),
};
let dest = format!("{}/{}", dir, basename); err |= domove(nam, movefn, src, &dest, flags); }
return err; }
if last_idx > 1 { zwarnnam(nam, "last of many arguments must be a directory"); return 1; }
let (src, dest) = if args.len() < 2 { let basename = match args[0].rsplit_once('/') { Some((_, n)) => n,
None => args[0].as_str(),
};
(args[0].clone(), basename.to_string()) } else {
(args[0].clone(), args[1].clone())
};
domove(nam, movefn, &src, &dest, flags) }
pub fn domove(nam: &str, movefn: MoveFunc, p: &str, q: &str, flags: i32) -> i32 { if (flags & MV_NODIRS) != 0 { match std::fs::symlink_metadata(p) { Ok(meta) if meta.is_dir() => { zwarnnam(nam, &format!("{}: is a directory", p)); return 1; }
Err(e) => {
zwarnnam(nam, &format!("{}: {}", p, e)); return 1;
}
_ => {}
}
}
if let Ok(qmeta) = std::fs::symlink_metadata(q) { let mut doit = (flags & MV_FORCE) != 0; if qmeta.is_dir() { zwarnnam(nam, &format!("{}: cannot overwrite directory", q)); return 1; } else if (flags & MV_INTERACTIVE) != 0 { eprint!("{}: replace `{}'? ", nam, q); if ask() == 0 { return 0; }
doit = true; } else if (flags & MV_ASKNW) != 0 && !qmeta.file_type().is_symlink() && unsafe { let cq = std::ffi::CString::new(q).ok();
cq.map(|c| libc::access(c.as_ptr(), libc::W_OK))
.unwrap_or(-1) != 0
} {
let mode = qmeta.permissions().mode() & 0o7777;
eprint!("{}: replace `{}', overriding mode {:04o}? ", nam, q, mode); if ask() == 0 { return 0; }
doit = true; }
if doit && (flags & MV_ATOMIC) == 0 { let _ = std::fs::remove_file(q); }
}
let r = { let cp = std::ffi::CString::new(p).unwrap_or_default();
let cq = std::ffi::CString::new(q).unwrap_or_default();
movefn(&cp, &cq)
};
if r != 0 { let osek = std::io::Error::last_os_error();
let errfile = if osek.raw_os_error() == Some(libc::ENOENT) && std::fs::symlink_metadata(p).is_ok() { q } else { p };
zwarnnam(nam, &format!("`{}': {}", errfile, osek)); return 1; }
0 }
pub struct recursivecmd<'a, P, R, L>
where
P: Fn(&str, &str, Option<&std::fs::Metadata>) -> i32,
R: Fn(&str, &str, Option<&std::fs::Metadata>) -> i32,
L: Fn(&str, &str, Option<&std::fs::Metadata>) -> i32,
{
pub nam: &'a str, pub opt_noerr: i32, pub opt_recurse: i32, pub opt_safe: i32, pub dirpre_func: P, pub dirpost_func: R, pub leaf_func: L, }
pub fn recursivecmd<P, R, L>( nam: &str, opt_noerr: i32, opt_recurse: i32, opt_safe: i32,
args: &[String], dirpre_func: P, dirpost_func: R, leaf_func: L,
) -> i32 where
P: Fn(&str, &str, Option<&std::fs::Metadata>) -> i32,
R: Fn(&str, &str, Option<&std::fs::Metadata>) -> i32,
L: Fn(&str, &str, Option<&std::fs::Metadata>) -> i32,
{
let _ = opt_noerr;
let reccmd = recursivecmd {
nam, opt_noerr, opt_recurse, opt_safe,
dirpre_func, dirpost_func, leaf_func,
};
let mut err = 0i32;
for arg in args { if (err & 2) != 0 { break; }
let first = if opt_safe != 0 { 0 } else { 1 }; err |= recursivecmd_doone(&reccmd, arg, arg, first); }
if err != 0 { 1 } else { 0 } }
pub fn recursivecmd_doone<P, R, L>( reccmd: &recursivecmd<P, R, L>, arg: &str, rp: &str, first: i32,
) -> i32 where
P: Fn(&str, &str, Option<&std::fs::Metadata>) -> i32,
R: Fn(&str, &str, Option<&std::fs::Metadata>) -> i32,
L: Fn(&str, &str, Option<&std::fs::Metadata>) -> i32,
{
let st = std::fs::symlink_metadata(rp); if reccmd.opt_recurse != 0 { if let Ok(ref meta) = st {
if meta.is_dir() { return recursivecmd_dorec(reccmd, arg, rp, meta, first); }
}
}
let sp = st.as_ref().ok(); (reccmd.leaf_func)(arg, rp, sp) }
pub fn recursivecmd_dorec<P, R, L>( reccmd: &recursivecmd<P, R, L>, arg: &str, rp: &str,
sp: &std::fs::Metadata, _first: i32,
) -> i32 where
P: Fn(&str, &str, Option<&std::fs::Metadata>) -> i32,
R: Fn(&str, &str, Option<&std::fs::Metadata>) -> i32,
L: Fn(&str, &str, Option<&std::fs::Metadata>) -> i32,
{
let err1 = (reccmd.dirpre_func)(arg, rp, Some(sp)); if (err1 & 2) != 0 { return 2; } let dir = match std::fs::read_dir(rp) { Ok(d) => d,
Err(e) => {
if reccmd.opt_noerr == 0 { zwarnnam(reccmd.nam, &format!("{}: {}", arg, e)); }
return err1 | 1; }
};
let mut err = err1;
for entry in dir.flatten() { let name = entry.file_name();
let name_str = name.to_string_lossy();
if name_str == "." || name_str == ".." { continue; } let narg = format!("{}/{}", arg.trim_end_matches('/'), name_str); let nrp = entry.path();
let nrp_str = nrp.to_string_lossy();
if (err & 2) != 0 { break; } err |= recursivecmd_doone(reccmd, &narg, &nrp_str, 0); }
if (err & 2) != 0 { return 2; } err | (reccmd.dirpost_func)(arg, rp, Some(sp)) }
pub fn recurse_donothing(_arg: &str, _rp: &str, _sp: Option<&std::fs::Metadata>) -> i32 {
0 }
pub struct rmmagic<'a> {
pub nam: &'a str, pub opt_force: i32, pub opt_interact: i32, pub opt_unlinkdir: i32, }
pub fn rm_leaf(arg: &str, rp: &str, sp: Option<&std::fs::Metadata>, rmm: &rmmagic) -> i32 {
if rmm.opt_unlinkdir == 0 || rmm.opt_force == 0 { let owned;
let sp_use = if let Some(s) = sp { Some(s) } else { owned = std::fs::symlink_metadata(rp).ok(); owned.as_ref()
};
if let Some(meta) = sp_use { if rmm.opt_unlinkdir == 0 && meta.is_dir() { if rmm.opt_force != 0 { return 0; } zwarnnam(rmm.nam, &format!("{}: is a directory", arg));
return 1; }
if rmm.opt_interact != 0 { eprint!("{}: remove `{}'? ", rmm.nam, arg); if ask() == 0 { return 0; } } else if rmm.opt_force == 0 && !meta.file_type().is_symlink() && unsafe { let crp = std::ffi::CString::new(rp).ok();
crp.map(|c| libc::access(c.as_ptr(), libc::W_OK))
.unwrap_or(-1) != 0
} {
let mode = meta.permissions().mode() & 0o7777;
eprint!("{}: remove `{}', overriding mode {:04o}? ",
rmm.nam, arg, mode); if ask() == 0 { return 0; } }
}
}
let crp = match std::ffi::CString::new(rp) {
Ok(c) => c,
Err(_) => return 1,
};
if unsafe { libc::unlink(crp.as_ptr()) } != 0 && rmm.opt_force == 0 { zwarnnam(rmm.nam, &format!("{}: {}", arg, std::io::Error::last_os_error()));
return 1; }
0 }
pub fn rm_dirpost(arg: &str, rp: &str, _sp: Option<&std::fs::Metadata>, rmm: &rmmagic) -> i32 {
let crp = match std::ffi::CString::new(rp) {
Ok(c) => c,
Err(_) => return 1,
};
if unsafe { libc::rmdir(crp.as_ptr()) } != 0 && rmm.opt_force == 0 { zwarnnam(rmm.nam, &format!("{}: {}", arg, std::io::Error::last_os_error()));
return 1; }
0 }
pub fn bin_rm(nam: &str, args: &[String], ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
let rmm = rmmagic {
nam, opt_force: if OPT_ISSET(ops, b'f') { 1 } else { 0 }, opt_interact: if OPT_ISSET(ops, b'i') && !OPT_ISSET(ops, b'f') { 1 } else { 0 },
opt_unlinkdir: if OPT_ISSET(ops, b'd') { 1 } else { 0 }, };
let recurse = if !OPT_ISSET(ops, b'd') && (OPT_ISSET(ops, b'R') || OPT_ISSET(ops, b'r')) { 1 } else { 0 };
let safe = if OPT_ISSET(ops, b's') { 1 } else { 0 }; let err = recursivecmd(nam, rmm.opt_force, recurse, safe, args, |_a, _r, _s| 0, |a, r, s| rm_dirpost(a, r, s, &rmm), |a, r, s| rm_leaf(a, r, s, &rmm)); if rmm.opt_force != 0 { 0 } else { err } }
pub struct chmodmagic<'a> {
pub nam: &'a str, pub mode: u32, }
pub fn chmod_dochmod(arg: &str, rp: &str, _sp: Option<&std::fs::Metadata>, chm: &chmodmagic) -> i32 {
let crp = match std::ffi::CString::new(rp) {
Ok(c) => c,
Err(_) => return 1,
};
if unsafe { libc::chmod(crp.as_ptr(), chm.mode as libc::mode_t) } != 0 { zwarnnam(chm.nam, &format!("{}: {}", arg, std::io::Error::last_os_error()));
return 1; }
0 }
pub fn bin_chmod(nam: &str, args: &[String], ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
if args.is_empty() {
zwarnnam(nam, "missing mode");
return 1;
}
let mode = match i64::from_str_radix(&args[0], 8) { Ok(m) => m as u32,
Err(_) => {
zwarnnam(nam, &format!("invalid mode `{}'", args[0])); return 1; }
};
let chm = chmodmagic { nam, mode };
let recurse = if OPT_ISSET(ops, b'R') { 1 } else { 0 }; let safe = if OPT_ISSET(ops, b's') { 1 } else { 0 }; recursivecmd(nam, 0, recurse, safe, &args[1..], |a, r, s| chmod_dochmod(a, r, s, &chm), |_a, _r, _s| 0, |a, r, s| chmod_dochmod(a, r, s, &chm)) }
pub struct chownmagic<'a> {
pub nam: &'a str, pub uid: i64, pub gid: i64, }
pub fn chown_dochown(arg: &str, rp: &str, _sp: Option<&std::fs::Metadata>, chm: &chownmagic) -> i32 {
let crp = match std::ffi::CString::new(rp) {
Ok(c) => c,
Err(_) => return 1,
};
let uid = if chm.uid < 0 { libc::uid_t::MAX } else { chm.uid as libc::uid_t };
let gid = if chm.gid < 0 { libc::gid_t::MAX } else { chm.gid as libc::gid_t };
if unsafe { libc::chown(crp.as_ptr(), uid, gid) } != 0 { zwarnnam(chm.nam, &format!("{}: {}", arg, std::io::Error::last_os_error()));
return 1; }
0 }
pub fn chown_dolchown(arg: &str, rp: &str, _sp: Option<&std::fs::Metadata>, chm: &chownmagic) -> i32 {
let crp = match std::ffi::CString::new(rp) {
Ok(c) => c,
Err(_) => return 1,
};
let uid = if chm.uid < 0 { libc::uid_t::MAX } else { chm.uid as libc::uid_t };
let gid = if chm.gid < 0 { libc::gid_t::MAX } else { chm.gid as libc::gid_t };
if unsafe { libc::lchown(crp.as_ptr(), uid, gid) } != 0 { zwarnnam(chm.nam, &format!("{}: {}", arg, std::io::Error::last_os_error()));
return 1; }
0 }
pub fn getnumeric(p: &str, errp: &mut i32) -> u64 { if !p.chars().next().is_some_and(|c| c.is_ascii_digit()) { *errp = 1; return 0; }
let end = p.find(|c: char| !c.is_ascii_digit()).unwrap_or(p.len());
let ret = p[..end].parse::<u64>().unwrap_or(0); *errp = if end < p.len() { 1 } else { 0 }; ret }
pub fn bin_chown(nam: &str, args: &[String], ops: &crate::ported::zsh_h::options, func: i32) -> i32 {
if args.is_empty() {
zwarnnam(nam, "missing argument");
return 1;
}
let uspec = args[0].clone(); let mut chm = chownmagic { nam, uid: -1, gid: -1 };
let mut p_idx = 0usize;
let mut do_group_only = false;
if func == BIN_CHGRP { chm.uid = -1; do_group_only = true; } else {
let end = uspec.find(':').or_else(|| uspec.find('.')); if end == Some(0) { chm.uid = -1; p_idx = 1; do_group_only = true; } else {
let user_part = if let Some(e) = end { &uspec[..e] } else { &uspec[..] };
let cuser = std::ffi::CString::new(user_part).unwrap_or_default();
let pwd = unsafe { libc::getpwnam(cuser.as_ptr()) }; let uid = if !pwd.is_null() {
unsafe { (*pwd).pw_uid as i64 } } else {
let mut errp = 0i32;
let n = getnumeric(user_part, &mut errp); if errp != 0 { zwarnnam(nam, &format!("{}: no such user", user_part));
return 1; }
n as i64
};
chm.uid = uid;
if let Some(e) = end { let group_part = &uspec[e + 1..];
if group_part.is_empty() { let p2 = if !pwd.is_null() { pwd } else { unsafe { libc::getpwuid(uid as libc::uid_t) } };
if p2.is_null() { zwarnnam(nam, &format!("{}: no such user", uspec));
return 1; }
chm.gid = unsafe { (*p2).pw_gid as i64 }; } else if group_part == ":" { chm.gid = -1; } else {
p_idx = 0; let cgrp = std::ffi::CString::new(group_part).unwrap_or_default();
let grp = unsafe { libc::getgrnam(cgrp.as_ptr()) }; if !grp.is_null() {
chm.gid = unsafe { (*grp).gr_gid as i64 }; } else {
let mut errp = 0i32;
let n = getnumeric(group_part, &mut errp); if errp != 0 { zwarnnam(nam, &format!("{}: no such group", group_part));
return 1; }
chm.gid = n as i64;
}
}
}
}
}
if do_group_only { let group_part = &uspec[p_idx..];
let cgrp = std::ffi::CString::new(group_part).unwrap_or_default();
let grp = unsafe { libc::getgrnam(cgrp.as_ptr()) }; if !grp.is_null() {
chm.gid = unsafe { (*grp).gr_gid as i64 }; } else {
let mut errp = 0i32;
let n = getnumeric(group_part, &mut errp); if errp != 0 { zwarnnam(nam, &format!("{}: no such group", group_part));
return 1; }
chm.gid = n as i64;
}
}
let recurse = if OPT_ISSET(ops, b'R') { 1 } else { 0 }; let safe = if OPT_ISSET(ops, b's') { 1 } else { 0 }; let h_flag = OPT_ISSET(ops, b'h'); recursivecmd(nam, 0, recurse, safe, &args[1..], |a, r, s| if h_flag { chown_dolchown(a, r, s, &chm) }
else { chown_dochown(a, r, s, &chm) }, |_a, _r, _s| 0, |a, r, s| if h_flag { chown_dolchown(a, r, s, &chm) }
else { chown_dochown(a, r, s, &chm) }) }
pub const LN_OPTS: &str = "dfhins";
use crate::ported::zsh_h::module;
#[allow(unused_variables)]
pub fn setup_(m: *const module) -> i32 { 0
}
pub fn features_(m: *const module, features: &mut Vec<String>) -> i32 { *features = featuresarray(m, module_features());
0
}
pub fn enables_(m: *const module, enables: &mut Option<Vec<i32>>) -> i32 { handlefeatures(m, module_features(), enables)
}
#[allow(unused_variables)]
pub fn boot_(m: *const module) -> i32 { 0
}
pub fn cleanup_(m: *const module) -> i32 { setfeatureenables(m, module_features(), None)
}
#[allow(unused_variables)]
pub fn finish_(m: *const module) -> i32 { 0
}
use crate::ported::zsh_h::features as features_t;
static MODULE_FEATURES: OnceLock<Mutex<features_t>> = OnceLock::new();
fn module_features() -> &'static Mutex<features_t> {
MODULE_FEATURES.get_or_init(|| Mutex::new(features_t {
bn_list: None,
bn_size: 18,
cd_list: None,
cd_size: 0,
mf_list: None,
mf_size: 0,
pd_list: None,
pd_size: 0,
n_abstract: 0,
}))
}
fn featuresarray(_m: *const module, _f: &Mutex<features_t>) -> Vec<String> {
vec!["b:chgrp".to_string(), "b:chmod".to_string(), "b:chown".to_string(), "b:ln".to_string(), "b:mkdir".to_string(), "b:mv".to_string(), "b:rm".to_string(), "b:rmdir".to_string(), "b:sync".to_string(), "b:zf_chgrp".to_string(), "b:zf_chmod".to_string(), "b:zf_chown".to_string(), "b:zf_ln".to_string(), "b:zf_mkdir".to_string(), "b:zf_mv".to_string(), "b:zf_rm".to_string(), "b:zf_rmdir".to_string(), "b:zf_sync".to_string()]
}
fn handlefeatures(
_m: *const module,
_f: &Mutex<features_t>,
enables: &mut Option<Vec<i32>>,
) -> i32 {
if enables.is_none() {
*enables = Some(vec![1; 18]);
}
0
}
fn setfeatureenables(
_m: *const module,
_f: &Mutex<features_t>,
_e: Option<&[i32]>,
) -> i32 {
0
}