use std::io::{self, Read, Write};
use std::time::Duration;
use std::sync::atomic::Ordering;
use std::cell::{Cell, RefCell};
use crate::ported::zle::zle_h::{
CURC_DEFAULT, CURC_INSERT, CURC_PENDING, CURC_REGION_END, CURC_REGION_START,
CURF_BAR, CURF_BLINK, CURF_BLOCK, CURF_BLUE_SHIFT, CURF_COLOR, CURF_COLOR_MASK,
CURF_GREEN_SHIFT, CURF_HIDDEN, CURF_RED_SHIFT, CURF_SHAPE_MASK, CURF_STEADY,
CURF_UNDERLINE,
};
thread_local! {
static CURSOR_FORMS: RefCell<Vec<u32>> =
RefCell::new(vec![0u32; CURC_DEFAULT as usize]);
static CURSOR_ENABLED_MASK: Cell<u32> = const { Cell::new(0) };
static CURSORFORM_SETUP: Cell<bool> = const { Cell::new(false) };
static AID: Cell<u32> = const { Cell::new(0) };
static PRE_BUFFER: RefCell<Vec<u8>> = RefCell::new(
b"\x1b]133;A;cl=m;aid=zZZZZZZ\x1b\\".to_vec()
);
}
#[allow(unused_imports)]
#[allow(unused_imports)]
use crate::ported::zle::zle_main::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_misc::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_hist::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_move::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_word::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_params::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_vi::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_utils::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_refresh::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_tricky::*;
#[allow(unused_imports)]
use crate::ported::zle::textobjects::*;
#[allow(unused_imports)]
use crate::ported::zle::deltochar::*;
const PROBE_TIMEOUT_MS: u64 = 500;
pub const TIMEOUT: i64 = -51;
pub const TAG: u8 = 1 << 7;
pub const SEQ: u8 = TAG | (1 << 6);
pub const T_BEGIN: u8 = 0x80; pub const T_END: u8 = 0x81; pub const T_OR: u8 = 0x82; pub const T_REPEAT: u8 = 0x83; pub const T_NUM: u8 = 0x84; pub const T_HEX: u8 = 0x85; pub const T_HEXCH: u8 = 0x86; pub const T_WILDCARD: u8 = 0x87; pub const T_RECORD: u8 = 0x88; pub const T_CAPTURE: u8 = 0x89; pub const T_DROP: u8 = 0x91; pub const T_CONTINUE: u8 = 0x92; pub const T_NEXT: u8 = 0x94;
pub fn query_terminal() { #[cfg(unix)]
{
if unsafe { libc::isatty(1) } != 1 {
return;
}
}
let _ = probe_terminal("\x1b[c", PROBE_TIMEOUT_MS);
}
fn probe_terminal(query: &str, timeout_ms: u64) -> io::Result<String> {
#[cfg(unix)]
{
let mut old_termios: libc::termios = unsafe { std::mem::zeroed() };
let has_old = unsafe { libc::tcgetattr(0, &mut old_termios) } == 0;
if has_old {
let mut raw = old_termios;
raw.c_lflag &= !(libc::ICANON | libc::ECHO);
raw.c_cc[libc::VMIN] = 0;
raw.c_cc[libc::VTIME] = (timeout_ms / 100).min(255) as u8;
unsafe { libc::tcsetattr(0, libc::TCSANOW, &raw) };
}
let _ = {
let fd = crate::ported::init::SHTTY.load(Ordering::Relaxed);
let out = if fd >= 0 { fd } else { 1 };
crate::ported::utils::write_loop(out, query.as_bytes())
};
let mut response = Vec::new();
let mut buf = [0u8; 1];
let deadline = std::time::Instant::now() + Duration::from_millis(timeout_ms);
while std::time::Instant::now() < deadline {
match io::stdin().read(&mut buf) {
Ok(1) => {
response.push(buf[0]);
if buf[0] == b'c'
|| buf[0] == b'n'
|| buf[0] == b't'
|| buf[0] == b'\\'
|| buf[0] == 0x07
{
break;
}
}
Ok(0) => break,
_ => break,
}
}
if has_old {
unsafe { libc::tcsetattr(0, libc::TCSANOW, &old_termios) };
}
Ok(String::from_utf8_lossy(&response).to_string())
}
#[cfg(not(unix))]
{
let _ = (query, timeout_ms);
Ok(String::new())
}
}
pub fn url_encode(s: &str) -> String {
let mut result = String::with_capacity(s.len() * 3);
for b in s.bytes() {
match b {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' | b'/' => {
result.push(b as char);
}
_ => {
result.push_str(&format!("%{:02X}", b));
}
}
}
result
}
pub fn system_clipget(clip: char) -> Option<String> { let mut seq = String::from("\x1b]52;.;?\x1b\\"); unsafe { seq.as_bytes_mut()[5] = clip as u8; } let reply = probe_terminal(&seq, 200).ok()?; let prefix = format!("\x1b]52;{};", clip);
let rest = reply.strip_prefix(&prefix)?;
let payload_end = rest.find('\x1b').or_else(|| rest.find('\x07'))?;
let b64 = &rest[..payload_end];
let bytes = base64_decode(b64);
String::from_utf8(bytes).ok() }
pub fn system_clipput(data: &str) -> String {
let mut buf = Vec::new();
{
let encoder = base64_encode(data.as_bytes());
buf.extend_from_slice(b"\x1b]52;c;");
buf.extend_from_slice(encoder.as_bytes());
buf.extend_from_slice(b"\x1b\\");
}
String::from_utf8_lossy(&buf).to_string()
}
fn base64_encode(data: &[u8]) -> String {
const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let mut result = String::with_capacity(data.len().div_ceil(3) * 4);
for chunk in data.chunks(3) {
let b0 = chunk[0] as u32;
let b1 = chunk.get(1).copied().unwrap_or(0) as u32;
let b2 = chunk.get(2).copied().unwrap_or(0) as u32;
let n = (b0 << 16) | (b1 << 8) | b2;
result.push(CHARS[((n >> 18) & 63) as usize] as char);
result.push(CHARS[((n >> 12) & 63) as usize] as char);
if chunk.len() > 1 {
result.push(CHARS[((n >> 6) & 63) as usize] as char);
} else {
result.push('=');
}
if chunk.len() > 2 {
result.push(CHARS[(n & 63) as usize] as char);
} else {
result.push('=');
}
}
result
}
pub fn extension_enabled(class: &str, ext: &str, def: bool) -> bool {
let elist: Vec<String> = {
let tab = match crate::ported::params::paramtab().read() {
Ok(t) => t,
Err(_) => return def,
};
match tab.get(".term.extensions") {
Some(pm) => pm.u_arr.clone().unwrap_or_default(),
None => Vec::new(),
}
};
for e in elist.iter() {
let (negate, body) = match e.strip_prefix('-') {
Some(rest) => (true, rest),
None => (false, e.as_str()),
};
if !body.starts_with(class) {
continue;
}
let after = &body[class.len()..];
if after.is_empty() {
return !negate;
}
if let Some(rest) = after.strip_prefix(':') {
if rest == ext {
return !negate;
}
}
}
def
}
pub fn zle_set_cursorform() { let atrs: Vec<String> = {
let tab = match crate::ported::params::paramtab().read() {
Ok(t) => t,
Err(_) => return,
};
match tab.get("zle_cursorform") {
Some(pm) => pm.u_arr.clone().unwrap_or_default(),
None => Vec::new(),
}
};
const CONTEXTS: [&str; 8] = [ "edit:",
"command:",
"insert:",
"overwrite:",
"pending:",
"regionstart:",
"regionend:",
"visual:",
];
CURSOR_FORMS.with(|cf| {
let mut f = cf.borrow_mut();
f.resize(CURC_DEFAULT as usize, 0);
for slot in f.iter_mut() {
*slot = 0;
}
f[CURC_INSERT as usize] = CURF_BAR as u32;
f[CURC_PENDING as usize] = CURF_UNDERLINE as u32;
});
for spec in atrs.iter() {
if let Some(rest) = spec.strip_prefix("region:") { let v = match_cursorform(rest);
CURSOR_FORMS.with(|cf| {
let mut f = cf.borrow_mut();
f[CURC_REGION_END as usize] = v;
f[CURC_REGION_START as usize] = v;
});
continue;
}
for (i, ctx) in CONTEXTS.iter().enumerate() {
if let Some(rest) = spec.strip_prefix(ctx) {
let v = match_cursorform(rest);
CURSOR_FORMS.with(|cf| {
cf.borrow_mut()[i] = v;
});
break;
}
}
}
let setup = CURSORFORM_SETUP.with(|s| s.get());
if !setup {
CURSORFORM_SETUP.with(|s| s.set(true));
let mut mask: u32 = 0;
if !extension_enabled("cursor", "shape", true) { mask |= (CURF_SHAPE_MASK as u32) | (CURF_BLINK as u32) | (CURF_STEADY as u32);
}
if !extension_enabled("cursor", "color", true) { mask |= CURF_COLOR_MASK;
}
CURSOR_ENABLED_MASK.with(|m| m.set(mask));
}
}
pub fn notify_pwd() { if !extension_enabled("integration", "pwd", true) {
return;
}
let hostnam = match crate::ported::params::getsparam("HOST") {
Some(h) if !h.contains('/') => h,
_ => return,
};
let pwd = crate::ported::params::getsparam("PWD").unwrap_or_default();
let shtty = crate::ported::init::SHTTY.load(Ordering::Relaxed);
if shtty < 0 {
return;
}
let _ = crate::ported::utils::write_loop(shtty, b"\x1b]7;file://");
write_urlencoded(shtty, &hostnam);
write_urlencoded(shtty, &pwd);
let _ = crate::ported::utils::write_loop(shtty, b"\x1b\\");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_url_encode() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(url_encode("/home/user"), "/home/user");
assert_eq!(url_encode("/path with spaces"), "/path%20with%20spaces");
assert_eq!(url_encode("hello&world"), "hello%26world");
}
#[test]
fn curf_constants_match_c() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
use crate::ported::zle::zle_h::{CURF_DEFAULT, CURF_UNDERLINE, CURF_BAR, CURF_BLOCK, CURF_SHAPE_MASK};
assert_eq!(CURF_DEFAULT, 0);
assert_eq!(CURF_UNDERLINE, 1);
assert_eq!(CURF_BAR, 2);
assert_eq!(CURF_BLOCK, 3);
assert_eq!(CURF_SHAPE_MASK, 3);
}
#[test]
fn match_cursorform_shapes_set_low_two_bits() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(match_cursorform("underline"), CURF_UNDERLINE as u32);
assert_eq!(match_cursorform("bar"), CURF_BAR as u32);
assert_eq!(match_cursorform("block"), CURF_BLOCK as u32);
assert_eq!(match_cursorform("none"), 0);
}
#[test]
fn match_cursorform_blink_steady_clear_each_other() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(
match_cursorform("blink,steady"),
CURF_STEADY as u32,
"steady should overwrite blink"
);
assert_eq!(
match_cursorform("steady,blink"),
CURF_BLINK as u32,
"blink should overwrite steady"
);
}
#[test]
fn match_cursorform_shape_plus_blink_compose() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let v = match_cursorform("bar,blink");
assert_eq!(v, CURF_BAR as u32 | CURF_BLINK as u32);
}
#[test]
fn match_cursorform_hidden_does_not_clobber_shape() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let v = match_cursorform("block,hidden");
assert_eq!(v, CURF_BLOCK as u32 | CURF_HIDDEN as u32);
}
#[test]
fn match_cursorform_color_4digit_nibble_form() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let v = match_cursorform("color=#0f80");
let expected = (CURF_COLOR as u32)
| (0xff_u32 << CURF_RED_SHIFT)
| (0x88_u32 << CURF_GREEN_SHIFT)
| (0x00_u32 << CURF_BLUE_SHIFT);
assert_eq!(v, expected);
}
#[test]
fn match_cursorform_color_6digit_left_shifts_by_8() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let v = match_cursorform("color=#abcdef");
assert_eq!(v, (0xabcdef_u32 << 8) | CURF_COLOR as u32);
}
#[test]
fn match_cursorform_unknown_component_skips_to_next_comma() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let v = match_cursorform("garbage,bar");
assert_eq!(v, CURF_BAR as u32);
}
#[test]
fn free_cursor_forms_nulls_storage_only() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
zle_set_cursorform();
let setup_before = CURSORFORM_SETUP.with(|s| s.get());
assert!(setup_before, "first zle_set_cursorform should flip setup");
let mask_before = CURSOR_ENABLED_MASK.with(|m| m.get());
free_cursor_forms();
CURSOR_FORMS.with(|cf| assert_eq!(cf.borrow().len(), 0));
assert!(CURSORFORM_SETUP.with(|s| s.get()),
"free_cursor_forms must NOT reset setup (C doesn't)");
assert_eq!(CURSOR_ENABLED_MASK.with(|m| m.get()), mask_before,
"free_cursor_forms must NOT clear enabled_mask (C doesn't)");
zle_set_cursorform();
CURSOR_FORMS.with(|cf| {
assert_eq!(cf.borrow()[CURC_INSERT as usize], CURF_BAR as u32);
});
}
#[test]
fn prompt_markers_computes_aid_and_splices_into_pre_buffer() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
AID.with(|a| a.set(0));
PRE_BUFFER.with(|p| {
*p.borrow_mut() = b"\x1b]133;A;cl=m;aid=zZZZZZZ\x1b\\".to_vec();
});
crate::ported::params::setsparam("HOST", "testhost");
let _ = prompt_markers();
let aid_after = AID.with(|a| a.get());
assert_ne!(aid_after, 0, "AID should be computed on first call");
let buf = PRE_BUFFER.with(|p| p.borrow().clone());
let spliced = &buf[13..19];
assert_ne!(spliced, b"ZZZZZZ", "pre[13..19] should be overwritten");
let expected_b64 = base64_encode(&aid_after.to_ne_bytes());
assert_eq!(spliced, &expected_b64.as_bytes()[..6]);
let before = AID.with(|a| a.get());
let _ = prompt_markers();
assert_eq!(AID.with(|a| a.get()), before, "AID stable after first call");
}
#[test]
fn prompt_markers_aid_collision_guard() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
AID.with(|a| a.set(0));
crate::ported::params::setsparam("HOST", "");
let _ = prompt_markers();
assert_ne!(AID.with(|a| a.get()), 0,
"AID must be nonzero after first call regardless of inputs");
}
#[test]
fn prompt_markers_shape_when_default_enabled() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let m = prompt_markers();
assert_eq!(m[0], "\x1b]133;P;k=i\x1b\\");
assert_eq!(m[1], "\x1b]133;P;k=s\x1b\\");
assert_eq!(m[2], "\x1b]133;P;k=r\x1b\\");
}
#[test]
fn extension_enabled_matches_whole_class() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
crate::ported::params::setaparam(
".term.extensions",
vec!["-integration".to_string()],
);
assert!(!extension_enabled("integration", "prompt", true));
assert!(!extension_enabled("integration", "pwd", true));
crate::ported::params::setaparam(".term.extensions", vec![]);
}
#[test]
fn extension_enabled_matches_specific_ext() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
crate::ported::params::setaparam(
".term.extensions",
vec!["-integration:pwd".to_string()],
);
assert!( extension_enabled("integration", "prompt", true));
assert!(!extension_enabled("integration", "pwd", true));
crate::ported::params::setaparam(".term.extensions", vec![]);
}
#[test]
fn extension_enabled_respects_default() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
crate::ported::params::setaparam(".term.extensions", vec![]);
assert!( extension_enabled("integration", "prompt", true));
assert!(!extension_enabled("integration", "prompt", false));
}
#[test]
fn zle_set_cursorform_seeds_default_slots() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
zle_set_cursorform();
CURSOR_FORMS.with(|cf| {
let f = cf.borrow();
assert_eq!(f[CURC_INSERT as usize], CURF_BAR as u32);
assert_eq!(f[CURC_PENDING as usize], CURF_UNDERLINE as u32);
assert_eq!(f[CURC_REGION_START as usize], 0);
assert_eq!(f[CURC_REGION_END as usize], 0);
});
}
#[test]
fn test_base64_encode() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(base64_encode(b"hello"), "aGVsbG8=");
assert_eq!(base64_encode(b""), "");
assert_eq!(base64_encode(b"a"), "YQ==");
}
#[test]
fn base64_decode_round_trip_with_encode() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let encoded = base64_encode(b"hello");
assert_eq!(base64_decode(&encoded), b"hello");
}
#[test]
fn base64_decode_well_known() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(base64_decode("TWFu"), b"Man");
}
#[test]
fn base64_decode_with_padding() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(base64_decode("YQ=="), b"a");
assert_eq!(base64_decode("YWI="), b"ab");
assert_eq!(base64_decode("YWJj"), b"abc");
}
#[test]
fn base64_decode_handles_plus_slash() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(base64_decode("+z/+"), vec![0xfb, 0x3f, 0xfe]);
}
#[test]
fn base64_decode_empty_input() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(base64_decode(""), Vec::<u8>::new());
}
}
pub fn base64_decode(src: &str) -> Vec<u8> { let bytes = src.as_bytes();
let len = bytes.len();
let mut buf = vec![0u8; (3 * len) / 4 + 1];
let mut i: usize = 0;
let mut b: usize = 0;
while i < len && bytes[i] != b'=' { let c = bytes[i];
let n: u32 = if c.is_ascii_digit() { (c - b'0') as u32 + 52
} else if c.is_ascii_lowercase() { (c - b'a') as u32 + 26
} else if c.is_ascii_uppercase() { (c - b'A') as u32
} else if c == b'+' { 62
} else if c == b'/' { 63
} else {
0
};
if i % 4 != 0 { buf[b] |= (n >> (2 * (3 - (i % 4)))) as u8;
b += 1;
}
i += 1; if i >= len { break; }
if b < buf.len() {
buf[b] = (n << (2 * (i % 4))) as u8;
}
}
buf.truncate(b);
buf }
pub fn collate_seq(_seq: &str) -> Vec<u8> { _seq.as_bytes().to_vec()
}
pub fn cursor_form() -> Vec<String> { Vec::new()
}
pub fn end_edit() -> i32 { 0
}
pub fn find_branch(s: &str, ch: u8) -> Option<usize> { s.bytes().position(|b| b == ch)
}
pub fn find_matching(s: &str, open: u8, close: u8) -> Option<usize> { let bytes = s.as_bytes();
let mut depth = 0i32;
for (i, &b) in bytes.iter().enumerate() {
if b == open {
depth += 1;
} else if b == close {
depth -= 1;
if depth == 0 {
return Some(i);
}
}
}
None
}
pub fn free_cursor_forms() { CURSOR_FORMS.with(|cf| cf.borrow_mut().clear());
}
pub fn handle_color(_seq: &str) -> i32 { 0
}
pub fn handle_paste(seq: &str, len: usize) -> String { let capture = if len > 0 && len <= seq.len() {
&seq[..len]
} else {
seq
};
let bytes = base64_decode(capture); String::from_utf8_lossy(&bytes).into_owned()
}
pub fn mark_output(start: bool) { const START: &[u8] = b"\x1b]133;C\x1b\\"; const END: &[u8] = b"\x1b]133;D\x1b\\"; if extension_enabled("integration", "output", true) { let shtty = crate::ported::init::SHTTY.load(Ordering::Relaxed);
if shtty < 0 { return; }
let _ = crate::ported::utils::write_loop( shtty,
if start { START } else { END },
);
}
}
pub fn match_cursorform(spec: &str) -> u32 { const SHAPES: &[(&str, u32, u32)] = &[
("none", 0, 0xff),
("underline", CURF_UNDERLINE as u32, CURF_SHAPE_MASK as u32),
("bar", CURF_BAR as u32, CURF_SHAPE_MASK as u32),
("block", CURF_BLOCK as u32, CURF_SHAPE_MASK as u32),
("blink", CURF_BLINK as u32, CURF_STEADY as u32),
("steady", CURF_STEADY as u32, CURF_BLINK as u32),
("hidden", CURF_HIDDEN as u32, 0),
];
let mut cursor_form: u32 = 0; let mut s = spec;
while !s.is_empty() {
let mut found = false;
if let Some(rest) = s.strip_prefix("color=#") { let mut end = 0;
for (i, ch) in rest.char_indices() {
if ch.is_ascii_hexdigit() {
end = i + ch.len_utf8();
} else {
break;
}
}
let hex_str = &rest[..end];
let col: u32 = u32::from_str_radix(hex_str, 16).unwrap_or(0);
if end == 4 { let red: u32 = col >> 8;
let green: u32 = (col & 0xf0) >> 4;
let blue: u32 = col & 0xf;
cursor_form &= 0xff; cursor_form |= (CURF_COLOR as u32)
| ((red << 4 | red) << CURF_RED_SHIFT)
| ((green << 4 | green) << CURF_GREEN_SHIFT)
| ((blue << 4 | blue) << CURF_BLUE_SHIFT);
found = true;
} else if end == 6 { cursor_form |= (col << 8) | (CURF_COLOR as u32); found = true;
}
s = &rest[end..];
}
if !found {
for (name, value, mask) in SHAPES {
if let Some(rest) = s.strip_prefix(name) {
cursor_form &= !*mask; cursor_form |= *value; s = rest;
found = true;
break;
}
}
}
if !found {
match s.find(',') {
Some(idx) => s = &s[idx..],
None => break,
}
}
let mut it = s.chars();
match it.next() {
Some(',') => s = it.as_str(),
_ => break,
}
}
cursor_form
}
pub fn prompt_markers() -> [String; 3] { if !extension_enabled("integration", "prompt", true) {
return [String::new(), String::new(), String::new()];
}
if AID.with(|a| a.get()) == 0 {
let host = crate::ported::params::getsparam("HOST").unwrap_or_default();
let h_hash = if host.is_empty() {
0
} else {
crate::ported::hashtable::hasher(&host)
};
let pid = unsafe { libc::getpid() } as u32;
let mut aid = h_hash ^ pid;
if aid == 0 {
aid = 1;
}
AID.with(|a| a.set(aid));
let aid_bytes = aid.to_ne_bytes();
let b64 = base64_encode(&aid_bytes);
PRE_BUFFER.with(|p| {
let mut buf = p.borrow_mut();
let payload = b64.as_bytes();
let n = payload.len().min(6).min(buf.len().saturating_sub(13));
for i in 0..n {
buf[13 + i] = payload[i];
}
});
}
[
"\x1b]133;P;k=i\x1b\\".to_string(), "\x1b]133;P;k=s\x1b\\".to_string(), "\x1b]133;P;k=r\x1b\\".to_string(), ]
}
pub fn start_edit() -> i32 { 0
}
pub fn write_urlencoded(fd: i32, path_components: &str) { let enc = url_encode(path_components);
let _ = crate::ported::utils::write_loop(fd, enc.as_bytes());
}
#[cfg(test)]
mod term_pat_tag_tests {
use super::*;
#[test]
fn tag_high_bit_set() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(TAG, 0x80);
assert_eq!(SEQ, 0xc0);
}
#[test]
fn t_constants_have_high_bit_set() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
for tag in [T_BEGIN, T_END, T_OR, T_REPEAT, T_NUM, T_HEX, T_HEXCH,
T_WILDCARD, T_RECORD, T_CAPTURE, T_DROP, T_CONTINUE, T_NEXT] {
assert!(tag & TAG != 0, "tag 0x{:02x} should have high bit set", tag);
}
}
#[test]
fn timeout_sentinel_negative() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(TIMEOUT, -51);
}
#[test]
fn t_repeat_in_seq_range() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert!((T_BEGIN..=T_HEXCH).contains(&T_REPEAT));
}
}