Skip to main content

zsh/ported/
zsh_h.rs

1//! `zsh.h` port — comprehensive umbrella header for the Rust port.
2//!
3//! Port of `Src/zsh.h` (3,375 lines). zsh.h is the umbrella header
4//! every C file `#include`s. Defines: integer types, tokenized
5//! character constants, ~50 typedef pointer aliases, ~64 structs,
6//! and hundreds of `#define` constants for parameters, builtins,
7//! redirections, jobs, pattern matching, options, terminal control,
8//! prompts, signals, history, completion, etc.
9//!
10//! Per the macro casing rule: UPPERCASE C macros stay UPPERCASE in
11//! Rust with `#[allow(non_snake_case)]`. Struct names use C casing
12//! verbatim with `#[allow(non_camel_case_types)]`. Reserved-keyword
13//! field names get a `_` suffix (`type` → `typ`, `str` → `str`,
14//! `match` → `match_`, `new` → `new_`, `loop` → `loop_`,
15//! `mod` → `mod_`, `fn` → `fn_`, `ref` → `ref_`).
16//!
17//! Many of the `typedef struct foo *Foo;` pointer aliases (c:510-549)
18//! reference structs whose canonical home is the matching `.c` file
19//! (e.g. `struct param` definition is in zsh.h:1829, but the live
20//! Rust storage is in `params.rs`). We define those structs here as
21//! the canonical port of zsh.h; consumers `use` them from here.
22
23use std::sync::atomic::{AtomicI32, Ordering};
24
25// =============================================================================
26// 1. Integer type aliases (zsh.h:30-92).
27// =============================================================================
28
29/// Port of `minimum` from `Src/zsh.h:31` — C macro `#define minimum(a,b)`.
30#[inline]
31#[allow(non_snake_case)]
32pub fn minimum<T: PartialOrd>(a: T, b: T) -> T {
33    // c:31
34    if a < b {
35        a
36    } else {
37        b
38    }
39}
40
41/// Port of `typedef ZSH_64_BIT_TYPE zlong;` from `Src/zsh.h:38`.
42/// On every modern platform this is `int64_t` / `i64`.
43#[allow(non_camel_case_types)]
44pub type zlong = i64; // c:38
45
46/// Port of `typedef ZSH_64_BIT_UTYPE zulong;` from `Src/zsh.h:50`.
47#[allow(non_camel_case_types)]
48pub type zulong = u64; // c:50
49
50/// Port of `#define ZLONG_MAX` from `Src/zsh.h:40-57`.
51pub const ZLONG_MAX: zlong = i64::MAX; // c:40-57
52
53// =============================================================================
54// 2. mnumber + math-fn types (zsh.h:94-136).
55// =============================================================================
56
57/// Port of `mnumber` from `Src/zsh.h:95-101`. C definition:
58///
59/// ```c
60/// typedef struct {
61///     union { zlong l; double d; } u;
62///     int type;
63/// } mnumber;
64/// ```
65///
66/// The C union is represented here with both alternatives held as
67/// sibling fields plus a discriminant — `type_` selects which side
68/// of the prior `u` union is live. Read `l` when
69/// `type_ == MN_INTEGER`, read `d` when `type_ == MN_FLOAT`.
70#[allow(non_camel_case_types)]
71#[derive(Debug, Clone, Copy)] // c:95
72pub struct mnumber {
73    // c:95
74    pub l: i64,     // c:97 (u.l)
75    pub d: f64,     // c:98 (u.d)
76    pub type_: u32, // c:100 (type)
77}
78/// `MN_INTEGER` from `Src/zsh.h:103`.
79pub const MN_INTEGER: u32 = 1; // c:103
80/// `MN_FLOAT` from `Src/zsh.h:104`.
81pub const MN_FLOAT: u32 = 2; // c:104
82/// `MN_UNSET` from `Src/zsh.h:105` — `mnumber not yet retrieved`.
83pub const MN_UNSET: u32 = 4; // c:105
84
85/// Port of `typedef struct mathfunc *MathFunc;` from `Src/zsh.h:107`.
86pub type MathFunc = Box<mathfunc>; // c:107
87
88/// Port of `typedef mnumber (*NumMathFunc)(...)` from `Src/zsh.h:108`.
89pub type NumMathFunc = fn(name: &str, argc: i32, argv: &[mnumber], id: i32) -> mnumber;
90
91/// Port of `typedef mnumber (*StrMathFunc)(...)` from `Src/zsh.h:109`.
92pub type StrMathFunc = fn(name: &str, arg: &str, id: i32) -> mnumber;
93
94/// Port of `struct mathfunc` from `Src/zsh.h:111-121`.
95#[allow(non_camel_case_types)]
96pub struct mathfunc {
97    // c:111
98    pub next: Option<Box<mathfunc>>, // c:112
99    pub name: String,                // c:113
100    pub flags: i32,                  // c:114
101    pub nfunc: Option<NumMathFunc>,  // c:115
102    pub sfunc: Option<StrMathFunc>,  // c:116
103    pub module: Option<String>,      // c:117
104    pub minargs: i32,                // c:118
105    pub maxargs: i32,                // c:119
106    pub funcid: i32,                 // c:120
107}
108/// `MFF_STR` constant.
109pub const MFF_STR: i32 = 1; // c:124
110/// `MFF_ADDED` constant.
111pub const MFF_ADDED: i32 = 2; // c:126
112/// `MFF_USERFUNC` constant.
113pub const MFF_USERFUNC: i32 = 4; // c:128
114/// `MFF_AUTOALL` constant.
115pub const MFF_AUTOALL: i32 = 8; // c:130
116
117// =============================================================================
118// 3. Meta byte + parser tokens (zsh.h:144-224).
119// =============================================================================
120
121// c:144 — `#define Meta ((char) 0x83)`. C `char` is one byte; every
122// use site in zsh compares against / writes raw bytes (`bytes[i] ==
123// Meta`, `out.push(Meta)`, `t[Meta]` ITOK-table indexing). The
124// previous `Meta: char = '\u{83}'` typing was the bot fakery —
125// Rust `char` is a 4-byte Unicode scalar, forcing 32+ `as u8` casts
126// across the tree. `u8` is the faithful byte type matching C's
127// `char` (which on every zsh build is a 1-byte type).
128pub const Meta: u8 = 0x83; // c:144
129/// `DEFAULT_IFS` constant.
130pub const DEFAULT_IFS: &str = " \t\n\u{83} "; // c:149
131/// `DEFAULT_IFS_SH` constant.
132pub const DEFAULT_IFS_SH: &str = " \t\n"; // c:153
133
134// `DEFAULT_FCEDIT` / `DEFAULT_HISTSIZE` belong to `config.h`, not
135// `zsh.h` — they live in `src/ported/config_h.rs` (per PORT.md
136// Rule C). Callers use `crate::ported::config_h::DEFAULT_FCEDIT` /
137// `crate::ported::config_h::DEFAULT_HISTSIZE` (cast `as i64` where
138// the destination is `histsiz: AtomicI64`).
139
140// Byte-token constants — names match C `Src/zsh.h:159-224` exactly
141// (PascalCase). One exception: C `String` (`0x85`) would shadow Rust's
142// `std::string::String`, so we use `Stringg` for that single token.
143#[allow(non_upper_case_globals)]
144pub const Pound: char = '\u{e184}'; // c:159 #
145pub const Stringg: char = '\u{e185}'; // c:160 $ — C `String` (renamed: collides with std::string::String)
146#[allow(non_upper_case_globals)]
147pub const Hat: char = '\u{e186}'; // c:161 ^
148#[allow(non_upper_case_globals)]
149pub const Star: char = '\u{e187}'; // c:162 *
150#[allow(non_upper_case_globals)]
151pub const Inpar: char = '\u{e188}'; // c:163 (
152#[allow(non_upper_case_globals)]
153pub const Inparmath: char = '\u{e189}'; // c:164 ((
154#[allow(non_upper_case_globals)]
155pub const Outpar: char = '\u{e18a}'; // c:165 )
156#[allow(non_upper_case_globals)]
157pub const Outparmath: char = '\u{e18b}'; // c:166 ))
158#[allow(non_upper_case_globals)]
159pub const Qstring: char = '\u{e18c}'; // c:167 "$"
160#[allow(non_upper_case_globals)]
161pub const Equals: char = '\u{e18d}'; // c:168 =
162#[allow(non_upper_case_globals)]
163pub const Bar: char = '\u{e18e}'; // c:169 |
164#[allow(non_upper_case_globals)]
165pub const Inbrace: char = '\u{e18f}'; // c:170 {
166#[allow(non_upper_case_globals)]
167pub const Outbrace: char = '\u{e190}'; // c:171 }
168#[allow(non_upper_case_globals)]
169pub const Inbrack: char = '\u{e191}'; // c:172 [
170#[allow(non_upper_case_globals)]
171pub const Outbrack: char = '\u{e192}'; // c:173 ]
172#[allow(non_upper_case_globals)]
173pub const Tick: char = '\u{e193}'; // c:174 `
174#[allow(non_upper_case_globals)]
175pub const Inang: char = '\u{e194}'; // c:175 <
176#[allow(non_upper_case_globals)]
177pub const Outang: char = '\u{e195}'; // c:176 >
178#[allow(non_upper_case_globals)]
179pub const OutangProc: char = '\u{e196}'; // c:177 >(...)
180#[allow(non_upper_case_globals)]
181pub const Quest: char = '\u{e197}'; // c:178 ?
182#[allow(non_upper_case_globals)]
183pub const Tilde: char = '\u{e198}'; // c:179 ~
184#[allow(non_upper_case_globals)]
185pub const Qtick: char = '\u{e199}'; // c:180 "`"
186#[allow(non_upper_case_globals)]
187pub const Comma: char = '\u{e19a}'; // c:181 ,
188#[allow(non_upper_case_globals)]
189pub const Dash: char = '\u{e19b}'; // c:182 -
190#[allow(non_upper_case_globals)]
191pub const Bang: char = '\u{e19c}'; // c:183 !
192/// `LAST_NORMAL_TOK` constant.
193pub const LAST_NORMAL_TOK: char = Bang; // c:188
194
195#[allow(non_upper_case_globals)]
196pub const Snull: char = '\u{e19d}'; // c:193
197#[allow(non_upper_case_globals)]
198pub const Dnull: char = '\u{e19e}'; // c:194
199#[allow(non_upper_case_globals)]
200pub const Bnull: char = '\u{e19f}'; // c:195
201#[allow(non_upper_case_globals)]
202pub const Bnullkeep: char = '\u{e1a0}'; // c:200
203#[allow(non_upper_case_globals)]
204pub const Nularg: char = '\u{e1a1}'; // c:206
205#[allow(non_upper_case_globals)]
206pub const Marker: char = '\u{e1a2}'; // c:224
207/// `SPECCHARS` constant.
208pub const SPECCHARS: &str = "#$^*()=|{}[]`<>?~;&\n\t \\\'\""; // c:228
209/// `PATCHARS` constant.
210pub const PATCHARS: &str = "#^*()|[]<>?~\\"; // c:232
211
212/// Port of `IS_DASH` from `Src/zsh.h:242` — C macro `#define IS_DASH(x)`.
213#[inline]
214#[allow(non_snake_case)]
215pub fn IS_DASH(x: char) -> bool {
216    x == '-' || x == Dash
217} // c:242
218
219// =============================================================================
220// 4. Quote types (zsh.h:252-294).
221// =============================================================================
222/// `QT_NONE` constant.
223pub const QT_NONE: i32 = 0; // c:257
224/// `QT_BACKSLASH` constant.
225pub const QT_BACKSLASH: i32 = 1; // c:259
226/// `QT_SINGLE` constant.
227pub const QT_SINGLE: i32 = 2; // c:261
228/// `QT_DOUBLE` constant.
229pub const QT_DOUBLE: i32 = 3; // c:263
230/// `QT_DOLLARS` constant.
231pub const QT_DOLLARS: i32 = 4; // c:265
232/// `QT_BACKTICK` constant.
233pub const QT_BACKTICK: i32 = 5; // c:271
234/// `QT_SINGLE_OPTIONAL` constant.
235pub const QT_SINGLE_OPTIONAL: i32 = 6; // c:276
236/// `QT_BACKSLASH_PATTERN` constant.
237pub const QT_BACKSLASH_PATTERN: i32 = 7; // c:282
238/// `QT_BACKSLASH_SHOWNULL` constant.
239pub const QT_BACKSLASH_SHOWNULL: i32 = 8; // c:286
240/// `QT_QUOTEDZPUTS` constant.
241pub const QT_QUOTEDZPUTS: i32 = 9; // c:291
242
243/// Port of `QT_IS_SINGLE` from `Src/zsh.h:294` — C macro `#define QT_IS_SINGLE(x)`.
244#[inline]
245#[allow(non_snake_case)]
246pub fn QT_IS_SINGLE(x: i32) -> bool {
247    x == QT_SINGLE || x == QT_SINGLE_OPTIONAL
248}
249
250// =============================================================================
251// 5. Lexical tokens (zsh.h:304-371).
252// =============================================================================
253/// `lextok` type alias.
254#[allow(non_camel_case_types)]
255pub type lextok = i32;
256/// `NULLTOK` constant.
257pub const NULLTOK: lextok = 0; // c:305
258/// `SEPER` constant.
259pub const SEPER: lextok = 1;
260/// `NEWLIN` constant.
261pub const NEWLIN: lextok = 2;
262/// `SEMI` constant.
263pub const SEMI: lextok = 3;
264/// `DSEMI` constant.
265pub const DSEMI: lextok = 4;
266/// `AMPER` constant.
267pub const AMPER: lextok = 5;
268/// `INPAR_TOK` constant.
269pub const INPAR_TOK: lextok = 6; // collision with char INPAR; suffix
270/// `OUTPAR_TOK` constant.
271pub const OUTPAR_TOK: lextok = 7;
272/// `DBAR` constant.
273pub const DBAR: lextok = 8;
274/// `DAMPER` constant.
275pub const DAMPER: lextok = 9;
276/// `OUTANG_TOK` constant.
277pub const OUTANG_TOK: lextok = 10; // collision with char OUTANG
278/// `OUTANGBANG` constant.
279pub const OUTANGBANG: lextok = 11;
280/// `DOUTANG` constant.
281pub const DOUTANG: lextok = 12;
282/// `DOUTANGBANG` constant.
283pub const DOUTANGBANG: lextok = 13;
284/// `INANG_TOK` constant.
285pub const INANG_TOK: lextok = 14;
286/// `INOUTANG` constant.
287pub const INOUTANG: lextok = 15;
288/// `DINANG` constant.
289pub const DINANG: lextok = 16;
290/// `DINANGDASH` constant.
291pub const DINANGDASH: lextok = 17;
292/// `INANGAMP` constant.
293pub const INANGAMP: lextok = 18;
294/// `OUTANGAMP` constant.
295pub const OUTANGAMP: lextok = 19;
296/// `AMPOUTANG` constant.
297pub const AMPOUTANG: lextok = 20;
298/// `OUTANGAMPBANG` constant.
299pub const OUTANGAMPBANG: lextok = 21;
300/// `DOUTANGAMP` constant.
301pub const DOUTANGAMP: lextok = 22;
302/// `DOUTANGAMPBANG` constant.
303pub const DOUTANGAMPBANG: lextok = 23;
304/// `TRINANG` constant.
305pub const TRINANG: lextok = 24;
306/// `BAR_TOK` constant.
307pub const BAR_TOK: lextok = 25;
308/// `BARAMP` constant.
309pub const BARAMP: lextok = 26;
310/// `INOUTPAR` constant.
311pub const INOUTPAR: lextok = 27;
312/// `DINPAR` constant.
313pub const DINPAR: lextok = 28;
314/// `DOUTPAR` constant.
315pub const DOUTPAR: lextok = 29;
316/// `AMPERBANG` constant.
317pub const AMPERBANG: lextok = 30;
318/// `SEMIAMP` constant.
319pub const SEMIAMP: lextok = 31;
320/// `SEMIBAR` constant.
321pub const SEMIBAR: lextok = 32;
322/// `DOUTBRACK` constant.
323pub const DOUTBRACK: lextok = 33;
324/// `STRING_LEX` constant.
325pub const STRING_LEX: lextok = 34;
326/// `ENVSTRING` constant.
327pub const ENVSTRING: lextok = 35;
328/// `ENVARRAY` constant.
329pub const ENVARRAY: lextok = 36;
330/// `ENDINPUT` constant.
331pub const ENDINPUT: lextok = 37;
332/// `LEXERR` constant.
333pub const LEXERR: lextok = 38;
334/// `BANG_TOK` constant.
335pub const BANG_TOK: lextok = 39; // c:346
336/// `DINBRACK` constant.
337pub const DINBRACK: lextok = 40;
338/// `INBRACE_TOK` constant.
339pub const INBRACE_TOK: lextok = 41;
340/// `OUTBRACE_TOK` constant.
341pub const OUTBRACE_TOK: lextok = 42;
342/// `CASE` constant.
343pub const CASE: lextok = 43;
344/// `COPROC` constant.
345pub const COPROC: lextok = 44;
346/// `DOLOOP` constant.
347pub const DOLOOP: lextok = 45;
348/// `DONE` constant.
349pub const DONE: lextok = 46;
350/// `ELIF` constant.
351pub const ELIF: lextok = 47;
352/// `ELSE` constant.
353pub const ELSE: lextok = 48;
354/// `ZEND` constant.
355pub const ZEND: lextok = 49;
356/// `ESAC` constant.
357pub const ESAC: lextok = 50;
358/// `FI` constant.
359pub const FI: lextok = 51;
360/// `FOR` constant.
361pub const FOR: lextok = 52;
362/// `FOREACH` constant.
363pub const FOREACH: lextok = 53;
364/// `FUNC` constant.
365pub const FUNC: lextok = 54;
366/// `IF` constant.
367pub const IF: lextok = 55;
368/// `NOCORRECT` constant.
369pub const NOCORRECT: lextok = 56;
370/// `REPEAT` constant.
371pub const REPEAT: lextok = 57;
372/// `SELECT` constant.
373pub const SELECT: lextok = 58;
374/// `THEN` constant.
375pub const THEN: lextok = 59;
376/// `TIME` constant.
377pub const TIME: lextok = 60;
378/// `UNTIL` constant.
379pub const UNTIL: lextok = 61;
380/// `WHILE` constant.
381pub const WHILE: lextok = 62;
382/// `TYPESET` constant.
383pub const TYPESET: lextok = 63; // c:370
384
385// =============================================================================
386// 6. Redirection types (zsh.h:377-408).
387// =============================================================================
388/// `REDIR_WRITE` constant.
389pub const REDIR_WRITE: i32 = 0;
390/// `REDIR_WRITENOW` constant.
391pub const REDIR_WRITENOW: i32 = 1;
392/// `REDIR_APP` constant.
393pub const REDIR_APP: i32 = 2;
394/// `REDIR_APPNOW` constant.
395pub const REDIR_APPNOW: i32 = 3;
396/// `REDIR_ERRWRITE` constant.
397pub const REDIR_ERRWRITE: i32 = 4;
398/// `REDIR_ERRWRITENOW` constant.
399pub const REDIR_ERRWRITENOW: i32 = 5;
400/// `REDIR_ERRAPP` constant.
401pub const REDIR_ERRAPP: i32 = 6;
402/// `REDIR_ERRAPPNOW` constant.
403pub const REDIR_ERRAPPNOW: i32 = 7;
404/// `REDIR_READWRITE` constant.
405pub const REDIR_READWRITE: i32 = 8;
406/// `REDIR_READ` constant.
407pub const REDIR_READ: i32 = 9;
408/// `REDIR_HEREDOC` constant.
409pub const REDIR_HEREDOC: i32 = 10;
410/// `REDIR_HEREDOCDASH` constant.
411pub const REDIR_HEREDOCDASH: i32 = 11;
412/// `REDIR_HERESTR` constant.
413pub const REDIR_HERESTR: i32 = 12;
414/// `REDIR_MERGEIN` constant.
415pub const REDIR_MERGEIN: i32 = 13;
416/// `REDIR_MERGEOUT` constant.
417pub const REDIR_MERGEOUT: i32 = 14;
418/// `REDIR_CLOSE` constant.
419pub const REDIR_CLOSE: i32 = 15;
420/// `REDIR_INPIPE` constant.
421pub const REDIR_INPIPE: i32 = 16;
422/// `REDIR_OUTPIPE` constant.
423pub const REDIR_OUTPIPE: i32 = 17;
424/// `REDIR_TYPE_MASK` constant.
425pub const REDIR_TYPE_MASK: i32 = 0x1f; // c:397
426/// `REDIR_VARID_MASK` constant.
427pub const REDIR_VARID_MASK: i32 = 0x20; // c:399
428/// `REDIR_FROM_HEREDOC_MASK` constant.
429pub const REDIR_FROM_HEREDOC_MASK: i32 = 0x40; // c:401
430/// Port of `IS_WRITE_FILE` from `Src/zsh.h:403` — C macro `#define IS_WRITE_FILE(X) ((X)>=REDIR_WRITE && (X)<=REDIR_READWRITE)`.
431#[inline]
432#[allow(non_snake_case)]
433pub fn IS_WRITE_FILE(x: i32) -> bool {
434    x >= REDIR_WRITE && x <= REDIR_READWRITE
435}
436/// Port of `IS_APPEND_REDIR` from `Src/zsh.h:404` — C macro `#define IS_APPEND_REDIR(X) (IS_WRITE_FILE(X) && ((X) & 2))`.
437#[inline]
438#[allow(non_snake_case)]
439pub fn IS_APPEND_REDIR(x: i32) -> bool {
440    IS_WRITE_FILE(x) && (x & 2) != 0
441}
442/// Port of `IS_CLOBBER_REDIR` from `Src/zsh.h:405` — C macro `#define IS_CLOBBER_REDIR(X) (IS_WRITE_FILE(X) && ((X) & 1))`.
443#[inline]
444#[allow(non_snake_case)]
445pub fn IS_CLOBBER_REDIR(x: i32) -> bool {
446    IS_WRITE_FILE(x) && (x & 1) != 0
447}
448/// Port of `IS_ERROR_REDIR` from `Src/zsh.h:406` — C macro `#define IS_ERROR_REDIR(X) ((X)>=REDIR_ERRWRITE && (X)<=REDIR_ERRAPPNOW)`.
449#[inline]
450#[allow(non_snake_case)]
451pub fn IS_ERROR_REDIR(x: i32) -> bool {
452    x >= REDIR_ERRWRITE && x <= REDIR_ERRAPPNOW
453}
454/// Port of `IS_READFD` from `Src/zsh.h:407` — C macro `#define IS_READFD(X) (((X)>=REDIR_READWRITE && (X)<=REDIR_MERGEIN) || (X)==REDIR_INPIPE)`.
455#[inline]
456#[allow(non_snake_case)]
457pub fn IS_READFD(x: i32) -> bool {
458    (x >= REDIR_READWRITE && x <= REDIR_MERGEIN) || x == REDIR_INPIPE
459}
460/// Port of `IS_REDIROP` from `Src/zsh.h:408` — C macro `#define IS_REDIROP(X) ((X)>=OUTANG && (X)<=TRINANG)`.
461#[inline]
462#[allow(non_snake_case)]
463pub fn IS_REDIROP(x: lextok) -> bool {
464    x >= OUTANG_TOK && x <= TRINANG
465}
466
467// =============================================================================
468// 7. fdtable values (zsh.h:415-465).
469// =============================================================================
470/// `FDT_UNUSED` constant.
471pub const FDT_UNUSED: i32 = 0; // c:416
472/// `FDT_INTERNAL` constant.
473pub const FDT_INTERNAL: i32 = 1; // c:421
474/// `FDT_EXTERNAL` constant.
475pub const FDT_EXTERNAL: i32 = 2; // c:426
476/// `FDT_MODULE` constant.
477pub const FDT_MODULE: i32 = 3; // c:433
478/// `FDT_XTRACE` constant.
479pub const FDT_XTRACE: i32 = 4; // c:437
480/// `FDT_FLOCK` constant.
481pub const FDT_FLOCK: i32 = 5; // c:441
482/// `FDT_FLOCK_EXEC` constant.
483pub const FDT_FLOCK_EXEC: i32 = 6; // c:446
484/// `FDT_PROC_SUBST` constant.
485pub const FDT_PROC_SUBST: i32 = 7; // c:454
486/// `FDT_TYPE_MASK` constant.
487pub const FDT_TYPE_MASK: i32 = 15; // c:458
488/// `FDT_SAVED_MASK` constant.
489pub const FDT_SAVED_MASK: i32 = 16; // c:465
490
491// =============================================================================
492// 8. Input-stack flags (zsh.h:468-476).
493// =============================================================================
494/// `INP_FREE` constant.
495pub const INP_FREE: i32 = 1 << 0; // c:468
496/// `INP_ALIAS` constant.
497pub const INP_ALIAS: i32 = 1 << 1; // c:469
498/// `INP_HIST` constant.
499pub const INP_HIST: i32 = 1 << 2; // c:470
500/// `INP_CONT` constant.
501pub const INP_CONT: i32 = 1 << 3; // c:471
502/// `INP_ALCONT` constant.
503pub const INP_ALCONT: i32 = 1 << 4; // c:472
504/// `INP_HISTCONT` constant.
505pub const INP_HISTCONT: i32 = 1 << 5; // c:473
506/// `INP_LINENO` constant.
507pub const INP_LINENO: i32 = 1 << 6; // c:474
508/// `INP_APPEND` constant.
509pub const INP_APPEND: i32 = 1 << 7; // c:475
510/// `INP_RAW_KEEP` constant.
511pub const INP_RAW_KEEP: i32 = 1 << 8; // c:476
512
513// =============================================================================
514// 9. metafy flags (zsh.h:479-486).
515// =============================================================================
516/// `META_REALLOC` constant.
517pub const META_REALLOC: i32 = 0; // c:479
518/// `META_USEHEAP` constant.
519pub const META_USEHEAP: i32 = 1;
520/// `META_STATIC` constant.
521pub const META_STATIC: i32 = 2;
522/// `META_DUP` constant.
523pub const META_DUP: i32 = 3;
524/// `META_ALLOC` constant.
525pub const META_ALLOC: i32 = 4;
526/// `META_NOALLOC` constant.
527pub const META_NOALLOC: i32 = 5;
528/// `META_HEAPDUP` constant.
529pub const META_HEAPDUP: i32 = 6;
530/// `META_HREALLOC` constant.
531pub const META_HREALLOC: i32 = 7;
532
533// =============================================================================
534// 10. ZCONTEXT_* (zsh.h:489-496) + entersubsh_ret (c:499-504).
535// =============================================================================
536/// `ZCONTEXT_HIST` constant.
537pub const ZCONTEXT_HIST: i32 = 1 << 0; // c:491
538/// `ZCONTEXT_LEX` constant.
539pub const ZCONTEXT_LEX: i32 = 1 << 1; // c:493
540/// `ZCONTEXT_PARSE` constant.
541pub const ZCONTEXT_PARSE: i32 = 1 << 2; // c:495
542/// `entersubsh_ret` — see fields for layout.
543#[derive(Default)]
544#[allow(non_camel_case_types)]
545pub struct entersubsh_ret {
546    // c:499
547    pub gleader: i32,       // c:501
548    pub list_pipe_job: i32, // c:503
549}
550
551// =============================================================================
552// 11. Linknode/linklist (zsh.h:557-572) + opaque pointer typedefs (c:510-549).
553// =============================================================================
554/// `linknode` — see fields for layout.
555#[allow(non_camel_case_types)]
556pub struct linknode {
557    // c:557
558    /// `next` field.
559    pub next: Option<Box<linknode>>,
560    /// `prev` field.
561    pub prev: Option<Box<linknode>>,
562    /// `dat` field.
563    pub dat: usize,
564}
565/// `linklist` — see fields for layout.
566#[allow(non_camel_case_types)]
567pub struct linklist {
568    // c:563
569    /// `first` field.
570    pub first: Option<Box<linknode>>,
571    /// `last` field.
572    pub last: Option<Box<linknode>>,
573    /// `flags` field.
574    pub flags: i32,
575}
576/// `LinkNode` type alias.
577pub type LinkNode = Box<linknode>; // c:533
578/// `LinkList` type alias.
579pub type LinkList = Box<linklist>; // c:534
580
581// Pointer typedefs for the ~50 struct types declared at c:510-549.
582// Each maps to a Box of the matching struct, with full field-by-field
583// body ports below (organized by C source line). Forward typedefs
584// here so structs that reference each other (e.g. param.old: Param)
585// can compile.
586/// `Alias` type alias.
587pub type Alias = Box<alias>; // c:510
588/// `Asgment` type alias.
589pub type Asgment = Box<asgment>; // c:511
590/// `Builtin` type alias.
591pub type Builtin = Box<builtin>; // c:512
592/// `Cmdnam` type alias.
593pub type Cmdnam = Box<cmdnam>; // c:513
594                               // `struct complist` body lives in `crate::ported::glob` (mirrors
595                               // C: declared in zsh.h via typedef alias, body defined in glob.c).
596                               // The `Complist` alias here resolves to that struct.
597/// `Complist` type alias.
598pub type Complist = Box<crate::ported::glob::complist>; // c:514
599/// `Conddef` type alias.
600pub type Conddef = Box<conddef>; // c:515
601/// `Dirsav` type alias.
602pub type Dirsav = Box<dirsav>; // c:516
603/// `Emulation_options` type alias.
604pub type Emulation_options = Box<emulation_options>; // c:517
605/// `Execcmd_params` type alias.
606pub type Execcmd_params = Box<execcmd_params>; // c:518
607/// `Features` type alias.
608pub type Features = Box<features>; // c:519
609/// `Feature_enables` type alias.
610pub type Feature_enables = Box<feature_enables>; // c:520
611/// `Funcstack` type alias.
612pub type Funcstack = Box<funcstack>; // c:521
613/// `FuncWrap` type alias.
614pub type FuncWrap = Box<funcwrap>; // c:522
615/// `HashNode` type alias.
616pub type HashNode = Box<hashnode>; // c:523
617/// `HashTable` type alias.
618pub type HashTable = Box<hashtable>; // c:524
619/// `Heap` type alias.
620pub type Heap = Box<heap>; // c:525
621/// `Heapstack` type alias.
622pub type Heapstack = Box<heapstack>; // c:526
623/// `Histent` type alias.
624pub type Histent = Box<histent>; // c:527
625/// `Hookdef` type alias.
626pub type Hookdef = Box<hookdef>; // c:528
627/// `Imatchdata` type alias.
628pub type Imatchdata = Box<imatchdata>; // c:529
629/// `Job` type alias.
630pub type Job = Box<job>; // c:531
631/// `Jobfile` type alias.
632pub type Jobfile = Box<jobfile>; // c:530
633/// `Linkedmod` type alias.
634pub type Linkedmod = Box<linkedmod>; // c:532
635/// `Module` type alias.
636pub type Module = Box<module>; // c:535
637/// `Nameddir` type alias.
638pub type Nameddir = Box<nameddir>; // c:536
639/// `Options` type alias.
640pub type Options = Box<options>; // c:537
641/// `Optname` type alias.
642pub type Optname = Box<optname>; // c:538
643/// `Param` type alias.
644pub type Param = Box<param>; // c:539
645/// `Paramdef` type alias.
646pub type Paramdef = Box<paramdef>; // c:540
647/// `Patstralloc` type alias.
648pub type Patstralloc = Box<patstralloc>; // c:541
649/// `Patprog` type alias.
650pub type Patprog = Box<patprog>; // c:542
651/// `Prepromptfn` type alias.
652pub type Prepromptfn = Box<prepromptfn>; // c:543
653/// `Process` type alias.
654pub type Process = Box<process>; // c:544
655/// `Redir` type alias.
656pub type Redir = Box<redir>; // c:545
657/// `Reswd` type alias.
658pub type Reswd = Box<reswd>; // c:546
659/// `Shfunc` type alias.
660pub type Shfunc = Box<shfunc>; // c:547
661/// `Timedfn` type alias.
662pub type Timedfn = Box<timedfn>; // c:548
663/// `Value` type alias.
664pub type Value = Box<value>; // c:549
665/// `voidvoidfnptr_t` type alias.
666pub type voidvoidfnptr_t = fn(); // c:621
667
668// Body-by-body struct definitions (C source order, fields verbatim
669// from zsh.h). Reserved-keyword Rust fields renamed minimally
670// (type→typ, str→str, match→match_, new→new_, loop→loop_,
671// mod→mod_, fn→fn_, ref→ref_, in→in_, where→where_).
672
673/// Port of `struct prepromptfn` from `Src/zsh.h:626-628`.
674#[allow(non_camel_case_types)]
675pub struct prepromptfn {
676    // c:626
677    /// `func` field.
678    pub func: voidvoidfnptr_t,
679}
680
681/// Port of `struct timedfn` from `Src/zsh.h:634-637`.
682#[allow(non_camel_case_types)]
683pub struct timedfn {
684    // c:634
685    /// `func` field.
686    pub func: voidvoidfnptr_t,
687    pub when: i64, // time_t
688}
689
690/// Port of `typedef int (*CondHandler)(...)` from `Src/zsh.h:681`.
691pub type CondHandler = fn(args: &[String], id: i32) -> i32;
692
693/// Port of `struct conddef` from `Src/zsh.h:683-692`.
694#[allow(non_camel_case_types)]
695pub struct conddef {
696    // c:683
697    pub next: Option<Conddef>,        // c:684
698    pub name: String,                 // c:685
699    pub flags: i32,                   // c:686 CONDF_*
700    pub handler: Option<CondHandler>, // c:687
701    pub min: i32,                     // c:688
702    pub max: i32,                     // c:689
703    pub condid: i32,                  // c:690
704    pub module: Option<String>,       // c:691
705}
706
707/// Port of `struct dirsav` from `Src/zsh.h:1159-1164`.
708#[allow(non_camel_case_types)]
709pub struct dirsav {
710    // c:1159
711    pub dirfd: i32,              // c:1160
712    pub level: i32,              // c:1160
713    pub dirname: Option<String>, // c:1161
714    pub dev: u64,                // c:1162 dev_t
715    pub ino: u64,                // c:1163 ino_t
716}
717
718/// Port of `struct hashnode` from `Src/zsh.h:1226-1230`.
719#[allow(non_camel_case_types)]
720#[derive(Debug, Clone, Default)]
721pub struct hashnode {
722    // c:1226
723    pub next: Option<HashNode>, // c:1227
724    pub nam: String,            // c:1228
725    pub flags: i32,             // c:1229
726}
727
728// hashtable function-pointer typedefs (zsh.h:1175-1193).
729/// `VFunc` type alias.
730pub type VFunc = fn(usize) -> usize; // c:1172
731/// `FreeFunc` type alias.
732pub type FreeFunc = fn(usize); // c:1173
733/// `HashFunc` type alias.
734pub type HashFunc = fn(name: &str) -> u32; // c:1175
735/// `TableFunc` type alias.
736pub type TableFunc = fn(table: &mut hashtable); // c:1176
737/// `AddNodeFunc` type alias.
738pub type AddNodeFunc = fn(table: &mut hashtable, name: String, val: usize);
739/// `GetNodeFunc` type alias.
740pub type GetNodeFunc = fn(table: &hashtable, name: &str) -> Option<HashNode>;
741/// `RemoveNodeFunc` type alias.
742pub type RemoveNodeFunc = fn(table: &mut hashtable, name: &str) -> Option<HashNode>;
743/// `FreeNodeFunc` type alias.
744pub type FreeNodeFunc = fn(node: HashNode);
745/// `CompareFunc` type alias.
746pub type CompareFunc = fn(a: &str, b: &str) -> i32;
747/// `ScanFunc` type alias. // c:1189
748pub type ScanFunc = fn(node: &HashNode, flags: i32);
749/// `ParamScanFunc` — the `ScanFunc` (c:1189) of a PARAM table's scan.
750///
751/// C has ONE `ScanFunc` typedef because it is polymorphic by cast: the
752/// `HashNode` a param-table scan hands its callback is really
753/// `&pm.node` of a fully populated `struct param`
754/// (`Src/Modules/parameter.c:483` sets `pm.node.nam`, `:489/:514` set
755/// `pm.u.str`, `:524` passes `&pm.node`), and the callback casts it
756/// straight back — `v.pm = (Param)hn` at `Src/params.c:671`, after
757/// which `getstrvalue(&v)` at `:694` reads the value the scan already
758/// put there. That is why `paramvalarr` (`Src/params.c:708-722`) runs
759/// exactly two passes, `scancountparams` then `scanparamvals`, and
760/// never calls a `getfn` a second time.
761///
762/// The `ScanFunc` slot inside `ScanTabFunc` is monomorphically that
763/// param flavour: `ht->scantab` is installed by exactly one function,
764/// `createspecialhash` (`Src/params.c:1241`), i.e. only on param
765/// tables. The plain-node flavour is what `disablenode` / `enablenode`
766/// / `printnode` (c:1217-1220) take, and those are never params.
767///
768/// Rust cannot express the `(Param)hn` cast, so the two flavours C
769/// distinguishes only by context get one typedef each. Passing the
770/// whole `&param` is what makes the value available to the callback,
771/// exactly as in C.
772pub type ParamScanFunc = fn(pm: &param, flags: i32);
773/// `ScanTabFunc` type alias. // c:1190
774pub type ScanTabFunc = fn(table: &hashtable, func: ParamScanFunc, flags: i32);
775/// `PrintTableStats` type alias.
776pub type PrintTableStats = fn(table: &hashtable);
777
778/// Port of `struct hashtable` from `Src/zsh.h:1200-1222`.
779#[allow(non_camel_case_types)]
780#[derive(Clone)]
781pub struct hashtable {
782    // c:1200
783    pub hsize: i32,                         // c:1202
784    pub ct: i32,                            // c:1203
785    pub nodes: Vec<Option<HashNode>>,       // c:1204
786    pub tmpdata: usize,                     // c:1205
787    pub hash: Option<HashFunc>,             // c:1208
788    pub emptytable: Option<TableFunc>,      // c:1209
789    pub filltable: Option<TableFunc>,       // c:1210
790    pub cmpnodes: Option<CompareFunc>,      // c:1211
791    pub addnode: Option<AddNodeFunc>,       // c:1212
792    pub getnode: Option<GetNodeFunc>,       // c:1213
793    pub getnode2: Option<GetNodeFunc>,      // c:1214
794    pub removenode: Option<RemoveNodeFunc>, // c:1216
795    pub disablenode: Option<ScanFunc>,      // c:1217
796    pub enablenode: Option<ScanFunc>,       // c:1218
797    pub freenode: Option<FreeNodeFunc>,     // c:1219
798    pub printnode: Option<ScanFunc>,        // c:1220
799    pub scantab: Option<ScanTabFunc>,       // c:1221
800}
801
802/// Port of `struct optname` from `Src/zsh.h:1239-1242`.
803#[allow(non_camel_case_types)]
804pub struct optname {
805    // c:1239
806    pub node: hashnode, // c:1240
807    pub optno: i32,     // c:1241
808}
809
810/// Port of `struct reswd` from `Src/zsh.h:1246-1249`.
811#[allow(non_camel_case_types)]
812#[derive(Debug, Clone)]
813pub struct reswd {
814    // c:1246
815    pub node: hashnode, // c:1247
816    pub token: i32,     // c:1248
817}
818
819/// Port of `struct alias` from `Src/zsh.h:1253-1257`.
820#[allow(non_camel_case_types)]
821#[derive(Debug)]
822pub struct alias {
823    // c:1253
824    pub node: hashnode, // c:1254
825    pub text: String,   // c:1255
826    /// c:1256 `int inuse;`
827    ///
828    /// !!! WARNING: ATOMIC, NOT A PLAIN `int` !!!
829    /// The alias tables are copy-on-write (`crate::cow_map::CowArc`) so
830    /// that `$( … )` can snapshot them in O(1). The lexer flips this
831    /// flag on EVERY alias expansion (`Src/lex.c:1929`, `Src/input.c:773`);
832    /// if that write needed `&mut` it would split — deep-copy — the whole
833    /// table the first time any substitution body expanded an alias.
834    /// Interior mutability lets the flag change through a shared node.
835    /// The flag is balanced within one expansion (set at c:1929, cleared
836    /// at c:773), so the snapshot sharing the node is left as it was.
837    pub inuse: std::sync::atomic::AtomicI32,
838}
839
840/// `inuse` is an atomic, so `Clone` is written out: every other field is
841/// a plain copy, exactly as `derive` produced before.
842impl Clone for alias {
843    fn clone(&self) -> Self {
844        alias {
845            node: self.node.clone(),
846            text: self.text.clone(),
847            inuse: std::sync::atomic::AtomicI32::new(
848                self.inuse.load(std::sync::atomic::Ordering::Relaxed),
849            ),
850        }
851    }
852}
853
854/// Port of `struct asgment` from `Src/zsh.h:1267-1275`. Note the C
855/// union is split into two Option<…> fields here; only one is set
856/// per asgment (dispatched by `flags & ASG_ARRAY`).
857///
858/// `array` is typed `LinkList<String>` (the generic port in
859/// `src/ported/linklist.rs`) rather than the bare `LinkList` type
860/// alias above — C stores `char *` payload in the list, so the
861/// typed form is what `firstnode`/`getdata`/`nextnode` traversal
862/// expects at every assign-printing site (e.g. `Src/builtin.c:476-482`).
863#[allow(non_camel_case_types)]
864pub struct asgment {
865    // c:1267
866    pub node: linknode,                                           // c:1268
867    pub name: String,                                             // c:1269
868    pub flags: i32,                                               // c:1270 ASG_*
869    pub scalar: Option<String>,                                   // c:1272 union value.scalar
870    pub array: Option<crate::ported::linklist::LinkList<String>>, // c:1273 union value.array (LinkList of char *)
871}
872
873/// Port of `struct cmdnam` from `Src/zsh.h:1301-1308`. The C union
874/// `{ char **name; char *cmd; }` becomes two Option fields; only
875/// one is set per cmdnam (dispatched by `flags & HASHED`).
876#[allow(non_camel_case_types)]
877#[derive(Debug, Clone)]
878pub struct cmdnam {
879    // c:1301
880    pub node: hashnode,            // c:1302
881    pub name: Option<Vec<String>>, // c:1304 union u.name
882    pub cmd: Option<String>,       // c:1305 union u.cmd
883}
884
885/// Port of `struct shfunc` from `Src/zsh.h:1316-1325`.
886#[allow(non_camel_case_types)]
887#[derive(Debug, Clone)]
888pub struct shfunc {
889    // c:1316
890    pub node: hashnode,                    // c:1317
891    pub filename: Option<String>,          // c:1318
892    pub lineno: i64,                       // c:1321 zlong
893    pub funcdef: Option<Eprog>,            // c:1322
894    pub redir: Option<Eprog>,              // c:1323
895    pub sticky: Option<Emulation_options>, // c:1324
896    /// **RUST-ONLY EXTENSION (no C counterpart).** Raw source text
897    /// for deferred-compile path: zshrs stores the function body
898    /// as-typed and parses on first invocation, vs C eagerly
899    /// compiling into `funcdef: Eprog` at definition time. When
900    /// the fusevm bytecode cache lands, this field gets retired in
901    /// favor of populating `funcdef` directly (matching C). Until
902    /// then, both fields can be set: `funcdef` for compiled
903    /// callers, `body` for the lazy-compile path.
904    pub body: Option<String>,
905    /// **RUST-ONLY EXTENSION (no C counterpart).** Rendered text of
906    /// `redir` for the deferred-compile path, the exact twin of `body`
907    /// standing in for `funcdef`. C keeps `f() { … } > out`'s trailing
908    /// redirections as a second Eprog in `redir` (c:Src/exec.c:5453
909    /// `shf->redir = dupeprog(redir_prog, 0)`) and re-deparses it with
910    /// `getpermtext(shf->redir, NULL, 1)` whenever the function is
911    /// printed (c:Src/hashtable.c:988-994,
912    /// c:Src/Modules/parameter.c:439-443). zshrs's fusevm path builds no
913    /// Eprog, so the compiler renders the redirection list to text once
914    /// (`getredirs` format, Src/text.c) and stores it here. Retired
915    /// alongside `body` when `redir` is populated for real.
916    pub redir_text: Option<String>,
917}
918
919/// Port of `struct funcstack` from `Src/zsh.h:1348-1356`.
920#[allow(non_camel_case_types)]
921#[derive(Clone, Default)]
922pub struct funcstack {
923    // c:1348
924    pub prev: Option<Funcstack>,  // c:1349
925    pub name: String,             // c:1350
926    pub filename: Option<String>, // c:1351
927    pub caller: Option<String>,   // c:1352
928    pub flineno: i64,             // c:1353
929    pub lineno: i64,              // c:1354
930    pub tp: i32,                  // c:1355 FS_*
931}
932
933/// Port of `typedef int (*WrapFunc)(...)` from `Src/zsh.h:1360`.
934pub type WrapFunc = fn(prog: Eprog, w: FuncWrap, name: &str) -> i32;
935
936/// Port of `struct funcwrap` from `Src/zsh.h:1362-1367`.
937#[allow(non_camel_case_types)]
938pub struct funcwrap {
939    // c:1362
940    pub next: Option<FuncWrap>,    // c:1363
941    pub flags: i32,                // c:1364
942    pub handler: Option<WrapFunc>, // c:1365
943    pub module: Option<Module>,    // c:1366
944}
945
946/// Port of `struct builtin` from `Src/zsh.h:1440-1448`.
947#[allow(non_camel_case_types)]
948pub struct builtin {
949    // c:1440
950    pub node: hashnode,                   // c:1441
951    pub handlerfunc: Option<HandlerFunc>, // c:1442
952    pub minargs: i32,                     // c:1443
953    pub maxargs: i32,                     // c:1444
954    pub funcid: i32,                      // c:1445
955    pub optstr: Option<String>,           // c:1446
956    pub defopts: Option<String>,          // c:1447
957}
958
959/// Port of `struct execcmd_params` from `Src/zsh.h:1492-1501`.
960///
961/// C's `Wordcode beg/varspc/assignspc` are `wordcode *` — raw pointers
962/// into the running wordcode stream owned by `state->prog`. Rust's
963/// safe analog is `usize` (index into `state.prog.prog`); `None`
964/// stands in for C's `NULL`. The wordcode bytes themselves stay in
965/// `state.prog` — eparams just records start offsets.
966#[allow(non_camel_case_types)]
967#[derive(Default)]
968pub struct execcmd_params {
969    // c:1492
970    pub args: Option<Vec<String>>, // c:1493 LinkList args
971    pub redir: Option<Vec<redir>>, // c:1494 LinkList redir
972    pub beg: usize,                // c:1495 Wordcode beg (pc index)
973    pub varspc: Option<usize>,     // c:1496 Wordcode varspc (NULL → None)
974    pub assignspc: Option<usize>,  // c:1497 Wordcode assignspc
975    pub typ: i32,                  // c:1498 (Rust keyword `type`)
976    pub postassigns: i32,          // c:1499
977    pub htok: i32,                 // c:1500
978}
979
980/// Port of `struct module` from `Src/zsh.h:1503-1513`. C uses a union
981/// for handle/linked/alias dispatched implicitly by load type; Rust
982/// port keeps three Options.
983///
984/// `autoloads`/`deps` are `LinkList<String>` because C's untyped
985/// `LinkList` carries `char *` payload for these fields (see
986/// `Src/module.c:2392` `zaddlinknode(m->deps, dep)` etc.).
987#[allow(non_camel_case_types)]
988#[derive(Debug)]
989pub struct module {
990    // c:1503
991    pub node: hashnode,                                               // c:1504
992    pub handle: Option<usize>,     // c:1506 union.handle (void *)
993    pub linked: Option<Linkedmod>, // c:1507 union.linked
994    pub alias: Option<String>,     // c:1508 union.alias
995    pub autoloads: Option<crate::ported::linklist::LinkList<String>>, // c:1510
996    pub deps: Option<crate::ported::linklist::LinkList<String>>, // c:1511
997    pub wrapper: i32,              // c:1512
998}
999
1000impl module {
1001    /// Construct a fresh statically-linked module entry. Mirrors C's
1002    /// `zshcalloc(sizeof(*m))` + `m->node.nam = ztrdup(name)` pattern
1003    /// at `Src/module.c:361` (`register_module`).
1004    pub fn new(name: &str) -> Self {
1005        Self {
1006            node: hashnode {
1007                next: None,
1008                nam: name.to_string(),
1009                flags: MOD_LINKED,
1010            },
1011            handle: None,
1012            linked: None,
1013            alias: None,
1014            autoloads: None,
1015            deps: None,
1016            wrapper: 0,
1017        }
1018    }
1019
1020    /// True if the module is currently usable.
1021    /// Mirrors C's `MOD_BUSY`/`MOD_UNLOAD` checks at `Src/module.c:1703`
1022    /// (`module_loaded`): the module exists and `MOD_UNLOAD` is clear.
1023    pub fn is_loaded(&self) -> bool {
1024        (self.node.flags & MOD_LINKED) != 0 && (self.node.flags & MOD_UNLOAD) == 0
1025    }
1026}
1027
1028/// Port of module fn-pointer typedefs from `Src/zsh.h:1534-1537`.
1029pub type Module_generic_func = fn() -> i32;
1030/// `Module_void_func` type alias.
1031pub type Module_void_func = fn(m: &module) -> i32;
1032/// `Module_features_func` type alias.
1033pub type Module_features_func = fn(m: &module, features: &mut Vec<String>) -> i32;
1034/// `Module_enables_func` type alias.
1035pub type Module_enables_func = fn(m: &module, enables: &mut Vec<i32>) -> i32;
1036
1037/// Port of `struct linkedmod` from `Src/zsh.h:1539-1547`.
1038#[allow(non_camel_case_types)]
1039#[derive(Debug)]
1040pub struct linkedmod {
1041    // c:1539
1042    pub name: String,                           // c:1540
1043    pub setup: Option<Module_void_func>,        // c:1541
1044    pub features: Option<Module_features_func>, // c:1542
1045    pub enables: Option<Module_enables_func>,   // c:1543
1046    pub boot: Option<Module_void_func>,         // c:1544
1047    pub cleanup: Option<Module_void_func>,      // c:1545
1048    pub finish: Option<Module_void_func>,       // c:1546
1049}
1050
1051/// Port of `struct features` from `Src/zsh.h:1553-1568`.
1052#[allow(non_camel_case_types)]
1053pub struct features {
1054    // c:1553
1055    pub bn_list: Option<Builtin>,  // c:1555
1056    pub bn_size: i32,              // c:1556
1057    pub cd_list: Option<Conddef>,  // c:1558
1058    pub cd_size: i32,              // c:1559
1059    pub mf_list: Option<MathFunc>, // c:1561
1060    pub mf_size: i32,              // c:1562
1061    pub pd_list: Option<Paramdef>, // c:1564
1062    pub pd_size: i32,              // c:1565
1063    pub n_abstract: i32,           // c:1567
1064}
1065
1066/// Port of `struct feature_enables` from `Src/zsh.h:1573-1578`.
1067#[allow(non_camel_case_types)]
1068pub struct feature_enables {
1069    // c:1573
1070    pub str: String,          // c:1575 (Rust keyword `str`)
1071    pub pat: Option<Patprog>, // c:1577
1072}
1073
1074/// Port of `typedef int (*Hookfn)(Hookdef, void *)` from `Src/zsh.h:1582`.
1075/// Real Rust fn-pointer matching the C ABI of a hook callback.
1076pub type Hookfn = fn(h: *mut hookdef, d: *mut std::ffi::c_void) -> i32;
1077
1078/// Port of `struct hookdef` from `Src/zsh.h:1584-1590`.
1079/// `next` and `funcs` are raw pointers matching the C
1080/// `Hookdef next` / `LinkList funcs` types exactly. NULL is represented
1081/// by `std::ptr::null_mut()`. Backing storage for hookdef nodes is
1082/// expected to be `Box::leak`'d (mirroring C's static-storage
1083/// `zshhooks[]` array and module-side `struct hookdef foo[] = { ... }`
1084/// constants).
1085#[allow(non_camel_case_types)]
1086pub struct hookdef {
1087    // c:1584
1088    pub next: *mut hookdef,   // c:1585 — struct hookdef *
1089    pub name: String,         // c:1586 — char *
1090    pub def: Option<Hookfn>,  // c:1587 — Hookfn (NULL = None)
1091    pub flags: i32,           // c:1588
1092    pub funcs: *mut linklist, // c:1589 — LinkList (struct linklist *)
1093}
1094// SAFETY: hookdef contains raw pointers. C zsh is single-threaded;
1095// zshrs serializes hook operations via the module-level `hooktab`
1096// AtomicPtr + the global_state_lock used by tests. Marking explicitly
1097// because raw pointers default to !Send/!Sync.
1098unsafe impl Send for hookdef {}
1099unsafe impl Sync for hookdef {}
1100
1101/// Port of `struct patprog` from `Src/zsh.h:1601-1611`.
1102///
1103/// C layout uses a trailing byte buffer accessed via
1104/// `(char *)prog + prog->startoff`; the Rust port stores it inline
1105/// as a `code: Vec<u8>` field. The opcode stream layout is preserved
1106/// byte-for-byte — `startoff` and `size` index into `code`.
1107///
1108#[derive(Debug, Clone)]
1109#[allow(non_camel_case_types)]
1110pub struct patprog {
1111    // c:1601
1112    pub startoff: i64,  // c:1602
1113    pub size: i64,      // c:1603
1114    pub mustoff: i64,   // c:1604
1115    pub patmlen: i64,   // c:1605
1116    pub globflags: i32, // c:1606
1117    pub globend: i32,   // c:1607
1118    pub flags: i32,     // c:1608 PAT_*
1119    pub patnpar: i32,   // c:1609
1120    pub patstartch: u8, // c:1610 (last field per zsh.h)
1121}
1122
1123/// Port of `struct patstralloc` from `Src/zsh.h:1613-1620`.
1124#[allow(non_camel_case_types)]
1125pub struct patstralloc {
1126    // c:1613
1127    pub unmetalen: i32,                // c:1614
1128    pub unmetalenp: i32,               // c:1615
1129    pub alloced: Option<String>,       // c:1617
1130    pub progstrunmeta: Option<String>, // c:1618
1131    pub progstrunmetalen: i32,         // c:1619
1132}
1133
1134/// Port of `struct zpc_disables_save` from `Src/zsh.h:1681-1689`.
1135#[allow(non_camel_case_types)]
1136pub struct zpc_disables_save {
1137    // c:1681
1138    pub next: Option<Box<zpc_disables_save>>, // c:1682
1139    pub disables: u32,                        // c:1688
1140}
1141/// `Zpc_disables_save` type alias.
1142pub type Zpc_disables_save = Box<zpc_disables_save>; // c:1691
1143
1144/// Port of `struct imatchdata` from `Src/zsh.h:1740-1760`.
1145#[allow(non_camel_case_types)]
1146pub struct imatchdata {
1147    // c:1740
1148    pub mstr: Option<String>,    // c:1742
1149    pub mlen: i32,               // c:1744
1150    pub ustr: Option<String>,    // c:1746
1151    pub ulen: i32,               // c:1748
1152    pub flags: i32,              // c:1750 SUB_*
1153    pub replstr: Option<String>, // c:1752
1154    // c:1759 — C `LinkList repllist` is a heterogeneous void* list of
1155    // `struct repldata`. The Rust `LinkList`/`linklist` substrate stores
1156    // a placeholder `dat: usize` and can't hold `repldata`, so the
1157    // faithful representation of "a list of repldata nodes" is a typed
1158    // Vec<repldata>. get_match_ret pushes onto it; the assembler reads it.
1159    pub repllist: Option<Vec<repldata>>, // c:1759
1160}
1161
1162// gsu_* function-pointer typedefs (zsh.h:1790-1794) + structs.
1163/// `GsuScalar` type alias.
1164pub type GsuScalar = Box<gsu_scalar>; // c:1790
1165/// `GsuInteger` type alias.
1166pub type GsuInteger = Box<gsu_integer>; // c:1791
1167/// `GsuFloat` type alias.
1168pub type GsuFloat = Box<gsu_float>; // c:1792
1169/// `GsuArray` type alias.
1170pub type GsuArray = Box<gsu_array>; // c:1793
1171/// `GsuHash` type alias.
1172pub type GsuHash = Box<gsu_hash>; // c:1794
1173/// `gsu_scalar` — see fields for layout.
1174#[allow(non_camel_case_types)]
1175#[derive(Clone)]
1176pub struct gsu_scalar {
1177    // c:1796
1178    pub getfn: fn(pm: &param) -> String,        // c:1797
1179    pub setfn: fn(pm: &mut param, val: String), // c:1798
1180    pub unsetfn: fn(pm: &mut param, exp: i32),  // c:1799
1181}
1182/// `gsu_integer` — see fields for layout.
1183#[allow(non_camel_case_types)]
1184#[derive(Clone)]
1185pub struct gsu_integer {
1186    // c:1802
1187    pub getfn: fn(pm: &param) -> i64,
1188    /// `setfn` field.
1189    pub setfn: fn(pm: &mut param, val: i64),
1190    /// `unsetfn` field.
1191    pub unsetfn: fn(pm: &mut param, exp: i32),
1192}
1193/// `gsu_float` — see fields for layout.
1194#[allow(non_camel_case_types)]
1195#[derive(Clone)]
1196pub struct gsu_float {
1197    // c:1808
1198    pub getfn: fn(pm: &param) -> f64,
1199    /// `setfn` field.
1200    pub setfn: fn(pm: &mut param, val: f64),
1201    /// `unsetfn` field.
1202    pub unsetfn: fn(pm: &mut param, exp: i32),
1203}
1204/// `gsu_array` — see fields for layout.
1205#[allow(non_camel_case_types)]
1206#[derive(Clone)]
1207pub struct gsu_array {
1208    // c:1814
1209    pub getfn: fn(pm: &param) -> Vec<String>,
1210    /// `setfn` field.
1211    pub setfn: fn(pm: &mut param, val: Vec<String>),
1212    /// `unsetfn` field.
1213    pub unsetfn: fn(pm: &mut param, exp: i32),
1214}
1215/// `gsu_hash` — see fields for layout.
1216#[allow(non_camel_case_types)]
1217#[derive(Clone)]
1218pub struct gsu_hash {
1219    // c:1820
1220    pub getfn: fn(pm: &param) -> Option<&HashTable>,
1221    /// `setfn` field.
1222    pub setfn: fn(pm: &mut param, val: HashTable),
1223    /// `unsetfn` field.
1224    pub unsetfn: fn(pm: &mut param, exp: i32),
1225}
1226
1227/// Port of `struct param` from `Src/zsh.h:1829-1867`. The C unions
1228/// `u` (data) and `gsu` (vtable) are flattened into per-variant
1229/// fields; the dispatcher looks at `node.flags & PM_TYPE` and reads
1230/// the matching field.
1231#[allow(non_camel_case_types)]
1232#[derive(Clone, Default)]
1233pub struct param {
1234    // c:1829
1235    pub node: hashnode, // c:1830
1236    // u union (c:1833-1842):
1237    pub u_data: usize, // c:1834 void *data
1238    // c:1834 — typed view of `u.data` for PM_TIED scalars, where C
1239    // stores a `struct tieddata *` (Src/zsh.h:1870-1873) carrying the
1240    // partner-array pointer and the joinchar from `typeset -T s a SEP`.
1241    pub u_tied: Option<Box<tieddata>>,
1242    pub u_arr: Option<Vec<String>>, // c:1835 char **arr
1243    pub u_str: Option<String>,      // c:1836 char *str
1244    pub u_val: i64,                 // c:1837 zlong val
1245    pub u_dval: f64,                // c:1839 double dval
1246    pub u_hash: Option<HashTable>,  // c:1841 HashTable hash
1247    // gsu vtable union (c:1852-1858):
1248    pub gsu_s: Option<GsuScalar>,  // c:1853
1249    pub gsu_i: Option<GsuInteger>, // c:1854
1250    pub gsu_f: Option<GsuFloat>,   // c:1855
1251    pub gsu_a: Option<GsuArray>,   // c:1856
1252    pub gsu_h: Option<GsuHash>,    // c:1857
1253    pub base: i32,                 // c:1860
1254    pub width: i32,                // c:1862
1255    pub env: Option<String>,       // c:1863
1256    pub ename: Option<String>,     // c:1864
1257    pub old: Option<Param>,        // c:1865
1258    pub level: i32,                // c:1866
1259}
1260
1261/// Port of `struct tieddata` from `Src/zsh.h:1870-1873`.
1262#[allow(non_camel_case_types)]
1263#[derive(Clone)]
1264pub struct tieddata {
1265    // c:1870
1266    pub arrptr: Option<Vec<String>>, // c:1871 char ***arrptr
1267    pub joinchar: i32,               // c:1872
1268}
1269
1270/// Port of `struct repldata` from `Src/zsh.h:2003-2006`.
1271#[allow(non_camel_case_types)]
1272pub struct repldata {
1273    // c:2003
1274    pub b: i32,                  // c:2004
1275    pub e: i32,                  // c:2004
1276    pub replstr: Option<String>, // c:2005
1277}
1278/// `Repldata` type alias.
1279pub type Repldata = Box<repldata>; // c:2007
1280
1281/// Port of `struct paramdef` from `Src/zsh.h:2082-2090`.
1282#[allow(non_camel_case_types)]
1283#[derive(Default)]
1284pub struct paramdef {
1285    // c:2082
1286    pub name: String,                 // c:2083
1287    pub flags: i32,                   // c:2084
1288    pub var: usize,                   // c:2085 void *
1289    pub gsu: usize,                   // c:2086 const void *
1290    pub getnfn: Option<GetNodeFunc>,  // c:2087
1291    pub scantfn: Option<ScanTabFunc>, // c:2088
1292    pub pm: Option<Param>,            // c:2089
1293}
1294
1295/// Port of `struct nameddir` from `Src/zsh.h:2149-2153`.
1296#[allow(non_camel_case_types)]
1297#[derive(Clone)]
1298pub struct nameddir {
1299    // c:2149
1300    pub node: hashnode, // c:2150
1301    pub dir: String,    // c:2151
1302    pub diff: i32,      // c:2152
1303}
1304
1305/// Port of `groupmap` from `Src/zsh.h:2161-2166`.
1306#[allow(non_camel_case_types)]
1307pub struct groupmap {
1308    // c:2161
1309    pub name: String, // c:2163
1310    pub gid: u32,     // c:2165 gid_t
1311}
1312/// `Groupmap` type alias.
1313pub type Groupmap = Box<groupmap>; // c:2167
1314
1315/// Port of `groupset` from `Src/zsh.h:2170-2175`.
1316#[allow(non_camel_case_types)]
1317pub struct groupset {
1318    // c:2170
1319    pub array: Vec<groupmap>, // c:2172
1320    pub num: i32,             // c:2174
1321}
1322/// `Groupset` type alias.
1323pub type Groupset = Box<groupset>; // c:2176
1324
1325/// Port of `struct histent` from `Src/zsh.h:2234-2250`.
1326#[allow(non_camel_case_types)]
1327pub struct histent {
1328    // c:2234
1329    pub node: hashnode,           // c:2235
1330    pub up: Option<Histent>,      // c:2237
1331    pub down: Option<Histent>,    // c:2238
1332    pub zle_text: Option<String>, // c:2239
1333    pub stim: i64,                // c:2244 time_t
1334    pub ftim: i64,                // c:2245
1335    pub words: Vec<i16>,          // c:2246
1336    pub nwords: i32,              // c:2248
1337    pub histnum: i64,             // c:2249 zlong
1338}
1339
1340/// Port of `struct emulation_options` from `Src/zsh.h:2570-2585`.
1341#[allow(non_camel_case_types)]
1342#[derive(Debug, Clone)]
1343pub struct emulation_options {
1344    // c:2570
1345    pub emulation: i32,          // c:2572
1346    pub n_on_opts: i32,          // c:2574
1347    pub n_off_opts: i32,         // c:2576
1348    pub on_opts: Vec<OptIndex>,  // c:2582
1349    pub off_opts: Vec<OptIndex>, // c:2584
1350}
1351
1352/// Port of `struct ttyinfo` from `Src/zsh.h:2593-2609`. The C
1353/// definition `#ifdef`-selects between `termios` / `termio` / sgtty.
1354/// Rust port stores the raw libc `termios` (the path taken on every
1355/// modern host).
1356#[allow(non_camel_case_types)]
1357#[derive(Debug, Clone)]
1358pub struct ttyinfo {
1359    // c:2593
1360    #[cfg(unix)]
1361    pub tio: libc::termios, // c:2595
1362    #[cfg(unix)]
1363    pub winsize: libc::winsize, // c:2607
1364}
1365
1366/// Port of `struct heapstack` from `Src/zsh.h:2871-2877`.
1367#[allow(non_camel_case_types)]
1368pub struct heapstack {
1369    // c:2871
1370    pub next: Option<Heapstack>, // c:2872
1371    pub used: usize,             // c:2873
1372}
1373
1374/// Port of `struct heap` from `Src/zsh.h:2881-2898`.
1375#[allow(non_camel_case_types)]
1376pub struct heap {
1377    // c:2881
1378    pub next: Option<Heap>,    // c:2882
1379    pub size: usize,           // c:2883
1380    pub used: usize,           // c:2884
1381    pub sp: Option<Heapstack>, // c:2885
1382}
1383
1384/// Port of `struct sortelt` from `Src/zsh.h:3013-3028`.
1385#[allow(non_camel_case_types)]
1386pub struct sortelt {
1387    // c:3013
1388    pub orig: String, // c:3013 `char *orig` — "The original string", still metafied.
1389    // c:3015 `const char *cmp` — "The string used for comparison".
1390    // BYTES, not a `String`: `strmetasort` (c:293-315) fills this by
1391    // UNMETAFYING the original, and an unmetafied byte string is not
1392    // valid UTF-8 in general — `$'a\xe9'` unmetafies to the two bytes
1393    // `61 e9`, which no `String` can hold. Holding the metafied
1394    // spelling instead handed `strcoll` `a\u{83}\u{c9}` and collated
1395    // the Meta payload as the accented letter it happens to spell.
1396    pub cmp: Vec<u8>,
1397    pub origlen: i32, // c:3020
1398    pub len: i32,     // c:3025
1399}
1400/// `SortElt` type alias.
1401pub type SortElt = Box<sortelt>; // c:3030
1402
1403/// Port of `struct hist_stack` from `Src/zsh.h:3037-3058`.
1404#[allow(non_camel_case_types)]
1405pub struct hist_stack {
1406    // c:3037
1407    pub histactive: i32,        // c:3038
1408    pub histdone: i32,          // c:3039
1409    pub stophist: i32,          // c:3040
1410    pub hlinesz: i32,           // c:3041
1411    pub defev: i64,             // c:3042 zlong
1412    pub hline: Option<String>,  // c:3043
1413    pub hptr: Option<String>,   // c:3044
1414    pub chwords: Vec<i16>,      // c:3045
1415    pub chwordlen: i32,         // c:3046
1416    pub chwordpos: i32,         // c:3047
1417    pub csp: i32,               // c:3056
1418    pub hist_keep_comment: i32, // c:3057
1419}
1420
1421/// Port of `struct lexbufstate` from `Src/zsh.h:3069-3079`.
1422#[allow(non_camel_case_types)]
1423#[derive(Debug, Clone, Default)]
1424pub struct lexbufstate {
1425    // c:3069
1426    pub ptr: Option<String>, // c:3074
1427    pub siz: i32,            // c:3076
1428    pub len: i32,            // c:3078
1429}
1430
1431/// Port of `struct lex_stack` from `Src/zsh.h:3082-3096`.
1432#[allow(non_camel_case_types)]
1433#[derive(Debug, Clone, Default)]
1434pub struct lex_stack {
1435    // c:3082
1436    pub dbparens: i32,              // c:3083
1437    pub isfirstln: i32,             // c:3084
1438    pub isfirstch: i32,             // c:3085
1439    pub lexflags: i32,              // c:3086
1440    pub tok: lextok,                // c:3087
1441    pub tokstr: Option<String>,     // c:3088
1442    pub zshlextext: Option<String>, // c:3089
1443    pub lexbuf: lexbufstate,        // c:3090
1444    pub lex_add_raw: i32,           // c:3091
1445    pub tokstr_raw: Option<String>, // c:3092
1446    pub lexbuf_raw: lexbufstate,    // c:3093
1447    pub lexstop: i32,               // c:3094
1448    pub toklineno: i64,             // c:3095
1449}
1450
1451/// Port of `struct parse_stack` from `Src/zsh.h:3099-3116`.
1452#[allow(non_camel_case_types)]
1453pub struct parse_stack {
1454    // c:3099
1455    pub hdocs: Option<Box<heredocs>>, // c:3100
1456    pub incmdpos: i32,                // c:3102
1457    pub aliasspaceflag: i32,          // c:3103
1458    pub incond: i32,                  // c:3104
1459    pub inredir: i32,                 // c:3105
1460    pub incasepat: i32,               // c:3106
1461    pub isnewlin: i32,                // c:3107
1462    pub infor: i32,                   // c:3108
1463    pub inrepeat_: i32,               // c:3109
1464    pub intypeset: i32,               // c:3110
1465    pub eclen: i32,                   // c:3112
1466    pub ecused: i32,                  // c:3112
1467    pub ecnpats: i32,                 // c:3112
1468    pub ecbuf: Wordcode,              // c:3113
1469    pub ecstrs: Option<Eccstr>,       // c:3114
1470    pub ecsoffs: i32,                 // c:3115
1471    pub ecssub: i32,                  // c:3115
1472    pub ecnfunc: i32,                 // c:3115
1473}
1474
1475/// Port of `struct heredocs` from `Src/zsh.h:1152-1157`. Used by
1476/// parse_stack above.
1477#[allow(non_camel_case_types)]
1478#[derive(Debug, Clone)]
1479pub struct heredocs {
1480    // c:1152
1481    pub next: Option<Box<heredocs>>, // c:1153
1482    pub typ: i32,                    // c:1154 (Rust keyword `type`)
1483    pub pc: i32,                     // c:1155
1484    pub str: Option<String>,         // c:1156
1485}
1486
1487/// Port of `struct execstack` from `Src/zsh.h:1127-1150`.
1488#[allow(non_camel_case_types)]
1489pub struct execstack {
1490    // c:1127
1491    pub next: Option<Box<execstack>>,      // c:1128
1492    pub list_pipe_pid: i32,                // c:1130 pid_t
1493    pub nowait: i32,                       // c:1131
1494    pub pline_level: i32,                  // c:1132
1495    pub list_pipe_child: i32,              // c:1133
1496    pub list_pipe_job: i32,                // c:1134
1497    pub list_pipe_text: [u8; JOBTEXTSIZE], // c:1135
1498    pub lastval: i32,                      // c:1136
1499    pub noeval: i32,                       // c:1137
1500    pub badcshglob: i32,                   // c:1138
1501    pub cmdoutpid: i32,                    // c:1139
1502    pub cmdoutval: i32,                    // c:1140
1503    pub use_cmdoutval: i32,                // c:1141
1504    pub procsubstpid: i32,                 // c:1142
1505    pub trap_return: i32,                  // c:1143
1506    pub trap_state: i32,                   // c:1144
1507    pub trapisfunc: i32,                   // c:1145
1508    pub traplocallevel: i32,               // c:1146
1509    pub noerrs: i32,                       // c:1147
1510    pub this_noerrexit: i32,               // c:1148
1511    pub underscore: Option<String>,        // c:1149
1512}
1513
1514/// Port of `struct process` from `Src/zsh.h:1117-1125`.
1515///
1516/// Field-shape deviations from the C struct (documented for the
1517/// `zsh.h ↔ zsh_h.rs` audit):
1518/// - `text`: `String` instead of `char text[JOBTEXTSIZE]`. The
1519///   `JOBTEXTSIZE` cap in C is a buffer-overflow guard; Rust's owned
1520///   String removes the cap without losing the field's semantic.
1521/// - `bgtime` / `endtime`: `Option<std::time::Instant>` instead of
1522///   `struct timespec`. C uses timespec for monotonic-clock points;
1523///   Rust's `Instant` is the equivalent abstraction.
1524/// - `next` removed: C threads `struct process *next` for the
1525///   in-job singly-linked list; Rust port owns the list externally
1526///   via `job.procs: Vec<process>` so callers don't carry the chain
1527///   pointer per node.
1528#[allow(non_camel_case_types)]
1529#[derive(Debug, Clone)]
1530pub struct process {
1531    // c:1117
1532    pub pid: i32,                            // c:1119 pid_t
1533    pub text: String,                        // c:1120 char text[JOBTEXTSIZE]
1534    pub status: i32,                         // c:1121
1535    pub ti: timeinfo,                        // c:1122 child_times_t ti
1536    pub bgtime: Option<std::time::Instant>,  // c:1123 struct timespec bgtime
1537    pub endtime: Option<std::time::Instant>, // c:1124 struct timespec endtime
1538}
1539
1540/// Port of `struct job` from `Src/zsh.h:1058-1071`.
1541///
1542/// Field-shape deviations from the C struct:
1543/// - `procs` / `auxprocs`: `Vec<process>` instead of singly-linked
1544///   `struct process *procs`. Equivalent semantics; Rust owns the
1545///   list ergonomically rather than threading `next` pointers.
1546/// - `filelist`: `Vec<String>` instead of `LinkList` of `char *`.
1547///   Same reasoning.
1548/// - `text`: added (Rust extension). C reconstructs job-display text
1549///   on demand by walking `procs->text`; the Rust port caches the
1550///   composed text here so display paths don't re-walk per call.
1551#[allow(non_camel_case_types)]
1552#[derive(Debug, Clone, Default)]
1553pub struct job {
1554    // c:1058
1555    pub gleader: i32,             // c:1059 pid_t
1556    pub other: i32,               // c:1060
1557    pub stat: i32,                // c:1062 STAT_*
1558    pub pwd: Option<String>,      // c:1063
1559    pub procs: Vec<process>,      // c:1065 struct process *procs
1560    pub auxprocs: Vec<process>,   // c:1066 struct process *auxprocs
1561    pub filelist: Vec<jobfile>,   // c:1067 LinkList filelist (elements are struct jobfile)
1562    pub stty_in_env: i32,         // c:1069
1563    pub ty: Option<Box<ttyinfo>>, // c:1070
1564    /// Rust extension: cached job-display text. C re-derives via
1565    /// `procs` walks in `printjob()` (`Src/jobs.c:1244+`).
1566    pub text: String,
1567}
1568
1569/// Port of `struct funcdump` from `Src/zsh.h:776-786`.
1570#[allow(non_camel_case_types)]
1571#[derive(Debug, Clone)]
1572pub struct funcdump {
1573    // c:776
1574    pub next: Option<FuncDump>,   // c:777
1575    pub dev: u64,                 // c:778 dev_t
1576    pub ino: u64,                 // c:779 ino_t
1577    pub fd: i32,                  // c:780
1578    pub map: Wordcode,            // c:781
1579    pub addr: Wordcode,           // c:782
1580    pub len: i32,                 // c:783
1581    pub count: i32,               // c:784
1582    pub filename: Option<String>, // c:785
1583}
1584
1585/// Port of `struct eprog` from `Src/zsh.h:805-815`.
1586#[allow(non_camel_case_types)]
1587#[derive(Debug, Clone, Default)]
1588pub struct eprog {
1589    // c:805
1590    pub flags: i32,             // c:806 EF_*
1591    pub len: i32,               // c:807
1592    pub npats: i32,             // c:808
1593    pub nref: i32,              // c:809
1594    pub pats: Vec<Patprog>,     // c:810
1595    pub prog: Wordcode,         // c:811
1596    pub strs: Option<String>,   // c:812
1597    pub shf: Option<Shfunc>,    // c:813
1598    pub dump: Option<FuncDump>, // c:814
1599    // !!! WARNING: RUST-ONLY FIELD — NO C COUNTERPART !!!
1600    // C keeps EVERY internal string metafied, so a .zwc string pool
1601    // needs no marker. zshrs keeps UTF-8 `String`s everywhere ELSE,
1602    // so the pool is the one place that still holds C-convention
1603    // bytes and the boundary has to be marked. Both producers set
1604    // this: `bld_eprog` (port of parse.c:547), because `ecstrcode`
1605    // (port of parse.c:426) metafies as it packs, which is also the
1606    // form `write_dump` copies straight to a `.zwc`; and
1607    // `check_dump_file` (port of parse.c:3833), which reads a
1608    // C-written pool verbatim. `ecgetstr` / `ecrawstr`
1609    // unmetafy per-string when this is set — whole-pool unmetafy is
1610    // impossible because wordcode words bake in byte offsets. It is
1611    // false only for an eprog with no pool at all. Without this,
1612    // every multibyte glyph whose UTF-8 has a byte in the IMETA range
1613    // decoded as tofu: █ (E2 96 88) is stored as E2 83 B6 88, whose
1614    // prefix is VALID UTF-8 (U+20F6 ⃶) — the zpwr banner bug, and the
1615    // `${functions[f]}` em-dash bug that came from `bld_eprog`
1616    // claiming its own metafied pool was clean.
1617    pub strs_metafied: bool,
1618}
1619
1620/// Port of `struct estate` from `Src/zsh.h:824-828`.
1621#[allow(non_camel_case_types)]
1622pub struct estate {
1623    // c:824
1624    pub prog: Eprog,          // c:825
1625    pub pc: usize,            // c:826 Wordcode pc — index into prog.prog (C is wordcode *)
1626    pub strs: Option<String>, // c:827 copy of prog.strs at estate creation
1627    pub strs_offset: usize,   // c:827 byte offset into strs — mirrors C `strs` pointer movement
1628}
1629
1630/// Port of `struct eccstr` from `Src/zsh.h:836-858`.
1631#[allow(non_camel_case_types)]
1632pub struct eccstr {
1633    // c:836
1634    pub left: Option<Eccstr>,  // c:838
1635    pub right: Option<Eccstr>, // c:838
1636    pub str: Option<String>,   // c:841
1637    pub offs: wordcode,        // c:844
1638    pub aoffs: wordcode,       // c:847
1639    pub nfunc: i32,            // c:854
1640    pub hashval: u32,          // c:857
1641}
1642
1643// =============================================================================
1644// 12. Z_* sublist flags (zsh.h:645-648).
1645// =============================================================================
1646/// `Z_TIMED` constant.
1647pub const Z_TIMED: i32 = 1 << 0; // c:645
1648/// `Z_SYNC` constant.
1649pub const Z_SYNC: i32 = 1 << 1; // c:646
1650/// `Z_ASYNC` constant.
1651pub const Z_ASYNC: i32 = 1 << 2; // c:647
1652/// `Z_DISOWN` constant.
1653pub const Z_DISOWN: i32 = 1 << 3; // c:648
1654
1655// =============================================================================
1656// 13. COND_* condition types (zsh.h:660-679).
1657// =============================================================================
1658/// `COND_NOT` constant.
1659pub const COND_NOT: i32 = 0;
1660/// `COND_AND` constant.
1661pub const COND_AND: i32 = 1;
1662/// `COND_OR` constant.
1663pub const COND_OR: i32 = 2;
1664/// `COND_STREQ` constant.
1665pub const COND_STREQ: i32 = 3;
1666/// `COND_STRDEQ` constant.
1667pub const COND_STRDEQ: i32 = 4;
1668/// `COND_STRNEQ` constant.
1669pub const COND_STRNEQ: i32 = 5;
1670/// `COND_STRLT` constant.
1671pub const COND_STRLT: i32 = 6;
1672/// `COND_STRGTR` constant.
1673pub const COND_STRGTR: i32 = 7;
1674/// `COND_NT` constant.
1675pub const COND_NT: i32 = 8;
1676/// `COND_OT` constant.
1677pub const COND_OT: i32 = 9;
1678/// `COND_EF` constant.
1679pub const COND_EF: i32 = 10;
1680/// `COND_EQ` constant.
1681pub const COND_EQ: i32 = 11;
1682/// `COND_NE` constant.
1683pub const COND_NE: i32 = 12;
1684/// `COND_LT` constant.
1685pub const COND_LT: i32 = 13;
1686/// `COND_GT` constant.
1687pub const COND_GT: i32 = 14;
1688/// `COND_LE` constant.
1689pub const COND_LE: i32 = 15;
1690/// `COND_GE` constant.
1691pub const COND_GE: i32 = 16;
1692/// `COND_REGEX` constant.
1693pub const COND_REGEX: i32 = 17;
1694/// `COND_MOD` constant.
1695pub const COND_MOD: i32 = 18;
1696/// `COND_MODI` constant.
1697pub const COND_MODI: i32 = 19;
1698/// `CONDF_INFIX` constant.
1699pub const CONDF_INFIX: i32 = 1; // c:695
1700/// `CONDF_ADDED` constant.
1701pub const CONDF_ADDED: i32 = 2; // c:697
1702/// `CONDF_AUTOALL` constant.
1703pub const CONDF_AUTOALL: i32 = 4; // c:699
1704
1705// =============================================================================
1706// 14. Redirection structures (zsh.h:706-740) + MULTIOUNIT.
1707// =============================================================================
1708/// `REDIRF_FROM_HEREDOC` constant.
1709pub const REDIRF_FROM_HEREDOC: i32 = 1; // c:708
1710/// `redir` — see fields for layout.
1711#[allow(non_camel_case_types)]
1712#[derive(Clone)]
1713pub struct redir {
1714    // c:713
1715    /// `typ` field.
1716    pub typ: i32,
1717    /// `flags` field.
1718    pub flags: i32,
1719    /// `fd1` field.
1720    pub fd1: i32,
1721    /// `fd2` field.
1722    pub fd2: i32,
1723    /// `name` field.
1724    pub name: Option<String>,
1725    /// `varid` field.
1726    pub varid: Option<String>,
1727    /// `here_terminator` field.
1728    pub here_terminator: Option<String>,
1729    /// `munged_here_terminator` field.
1730    pub munged_here_terminator: Option<String>,
1731}
1732/// `MULTIOUNIT` constant.
1733pub const MULTIOUNIT: usize = 8; // c:725
1734/// `multio` — see fields for layout.
1735#[allow(non_camel_case_types)]
1736pub struct multio {
1737    // c:735
1738    /// `ct` field.
1739    pub ct: i32,
1740    /// `rflag` field.
1741    pub rflag: i32,
1742    /// `pipe` field.
1743    pub pipe: i32,
1744    /// C `int fds[1]` with `VARLENARRAY` trailing-element realloc via
1745    /// `hrealloc(mn, sizeof + ct*sizeof(int))`. Rust uses a growable
1746    /// `Vec<i32>` (no MULTIOUNIT cap). Initial slot stamped on
1747    /// construction (c:2449).
1748    pub fds: Vec<i32>,
1749}
1750
1751// =============================================================================
1752// 15. value struct (zsh.h:744-755) + VALFLAG_* + MAX_ARRLEN.
1753// =============================================================================
1754/// `value` — see fields for layout.
1755#[allow(non_camel_case_types)]
1756pub struct value {
1757    // c:744
1758    /// `pm` field.
1759    pub pm: Option<Param>,
1760    /// `arr` field.
1761    pub arr: Vec<String>,
1762    /// `scanflags` field.
1763    pub scanflags: i32,
1764    /// `valflags` field.
1765    pub valflags: i32,
1766    /// `start` field.
1767    pub start: i32,
1768    /// `end` field.
1769    pub end: i32,
1770}
1771/// `VALFLAG_INV` constant.
1772pub const VALFLAG_INV: i32 = 0x0001; // c:758
1773/// `VALFLAG_EMPTY` constant.
1774pub const VALFLAG_EMPTY: i32 = 0x0002;
1775/// `VALFLAG_SUBST` constant.
1776pub const VALFLAG_SUBST: i32 = 0x0004;
1777/// `VALFLAG_REFSLICE` constant.
1778pub const VALFLAG_REFSLICE: i32 = 0x0008;
1779/// `MAX_ARRLEN` constant.
1780pub const MAX_ARRLEN: i32 = 262144; // c:764
1781
1782// =============================================================================
1783// 16. Word code types (zsh.h:770-1038).
1784// =============================================================================
1785/// `wordcode` type alias.
1786#[allow(non_camel_case_types)]
1787pub type wordcode = u32; // c:770
1788/// `Wordcode` type alias.
1789pub type Wordcode = Vec<wordcode>; // c:771
1790/// `FuncDump` type alias.
1791pub type FuncDump = Box<funcdump>; // c:773
1792/// `Eprog` type alias.
1793pub type Eprog = Box<eprog>; // c:774
1794/// `EF_REAL` constant.
1795pub const EF_REAL: i32 = 1; // c:817
1796/// `EF_HEAP` constant.
1797pub const EF_HEAP: i32 = 2;
1798/// `EF_MAP` constant.
1799pub const EF_MAP: i32 = 4;
1800/// `EF_RUN` constant.
1801pub const EF_RUN: i32 = 8;
1802/// `Estate` type alias.
1803pub type Estate = Box<estate>; // c:822
1804/// `Eccstr` type alias.
1805pub type Eccstr = Box<eccstr>; // c:835
1806/// `EC_NODUP` constant.
1807pub const EC_NODUP: i32 = 0; // c:869
1808/// `EC_DUP` constant.
1809pub const EC_DUP: i32 = 1; // c:872
1810/// `EC_DUPTOK` constant.
1811pub const EC_DUPTOK: i32 = 2; // c:878
1812/// `WC_CODEBITS` constant.
1813pub const WC_CODEBITS: u32 = 5; // c:882
1814/// Port of `wc_code` from `Src/zsh.h:883` — C macro `#define wc_code(C) ((C) & ((wordcode) ((1 << WC_CODEBITS) - 1)))`.
1815#[inline]
1816#[allow(non_snake_case)]
1817pub fn wc_code(c: wordcode) -> wordcode {
1818    c & ((1 << WC_CODEBITS) - 1)
1819}
1820/// Port of `wc_data` from `Src/zsh.h:884` — C macro `#define wc_data(C) ((C) >> WC_CODEBITS)`.
1821#[inline]
1822#[allow(non_snake_case)]
1823pub fn wc_data(c: wordcode) -> wordcode {
1824    c >> WC_CODEBITS
1825}
1826/// Port of `wc_bdata` from `Src/zsh.h:885` — C macro `#define wc_bdata(D) ((D) << WC_CODEBITS)`.
1827#[inline]
1828#[allow(non_snake_case)]
1829pub fn wc_bdata(d: wordcode) -> wordcode {
1830    d << WC_CODEBITS
1831}
1832/// Port of `wc_bld` from `Src/zsh.h:886` — C macro `#define wc_bld(C,D) (((wordcode) (C)) | (((wordcode) (D)) << WC_CODEBITS))`.
1833#[inline]
1834#[allow(non_snake_case)]
1835pub fn wc_bld(c: wordcode, d: wordcode) -> wordcode {
1836    c | (d << WC_CODEBITS)
1837}
1838/// `WC_END` constant.
1839pub const WC_END: wordcode = 0;
1840/// `WC_LIST` constant.
1841pub const WC_LIST: wordcode = 1;
1842/// `WC_SUBLIST` constant.
1843pub const WC_SUBLIST: wordcode = 2;
1844/// `WC_PIPE` constant.
1845pub const WC_PIPE: wordcode = 3;
1846/// `WC_REDIR` constant.
1847pub const WC_REDIR: wordcode = 4;
1848/// `WC_ASSIGN` constant.
1849pub const WC_ASSIGN: wordcode = 5;
1850/// `WC_SIMPLE` constant.
1851pub const WC_SIMPLE: wordcode = 6;
1852/// `WC_TYPESET` constant.
1853pub const WC_TYPESET: wordcode = 7;
1854/// `WC_SUBSH` constant.
1855pub const WC_SUBSH: wordcode = 8;
1856/// `WC_CURSH` constant.
1857pub const WC_CURSH: wordcode = 9;
1858/// `WC_TIMED` constant.
1859pub const WC_TIMED: wordcode = 10;
1860/// `WC_FUNCDEF` constant.
1861pub const WC_FUNCDEF: wordcode = 11;
1862/// `WC_FOR` constant.
1863pub const WC_FOR: wordcode = 12;
1864/// `WC_SELECT` constant.
1865pub const WC_SELECT: wordcode = 13;
1866/// `WC_WHILE` constant.
1867pub const WC_WHILE: wordcode = 14;
1868/// `WC_REPEAT` constant.
1869pub const WC_REPEAT: wordcode = 15;
1870/// `WC_CASE` constant.
1871pub const WC_CASE: wordcode = 16;
1872/// `WC_IF` constant.
1873pub const WC_IF: wordcode = 17;
1874/// `WC_COND` constant.
1875pub const WC_COND: wordcode = 18;
1876/// `WC_ARITH` constant.
1877pub const WC_ARITH: wordcode = 19;
1878/// `WC_AUTOFN` constant.
1879pub const WC_AUTOFN: wordcode = 20;
1880/// `WC_TRY` constant.
1881pub const WC_TRY: wordcode = 21;
1882/// `WC_COUNT` constant.
1883pub const WC_COUNT: wordcode = 22;
1884/// `Z_END` constant.
1885pub const Z_END: i32 = 1 << 4; // c:921
1886/// `Z_SIMPLE` constant.
1887pub const Z_SIMPLE: i32 = 1 << 5; // c:922
1888/// `WC_LIST_FREE` constant.
1889pub const WC_LIST_FREE: u32 = 6; // c:923
1890/// `WC_SUBLIST_END` constant.
1891pub const WC_SUBLIST_END: wordcode = 0;
1892/// `WC_SUBLIST_AND` constant.
1893pub const WC_SUBLIST_AND: wordcode = 1;
1894/// `WC_SUBLIST_OR` constant.
1895pub const WC_SUBLIST_OR: wordcode = 2;
1896/// `WC_SUBLIST_COPROC` constant.
1897pub const WC_SUBLIST_COPROC: wordcode = 4;
1898/// `WC_SUBLIST_NOT` constant.
1899pub const WC_SUBLIST_NOT: wordcode = 8;
1900/// `WC_SUBLIST_SIMPLE` constant.
1901pub const WC_SUBLIST_SIMPLE: wordcode = 16;
1902/// `WC_SUBLIST_FREE` constant.
1903pub const WC_SUBLIST_FREE: u32 = 5; // c:935
1904/// `WC_PIPE_END` constant.
1905pub const WC_PIPE_END: wordcode = 0;
1906/// `WC_PIPE_MID` constant.
1907pub const WC_PIPE_MID: wordcode = 1;
1908/// `WC_ASSIGN_SCALAR` constant.
1909pub const WC_ASSIGN_SCALAR: wordcode = 0;
1910/// `WC_ASSIGN_ARRAY` constant.
1911pub const WC_ASSIGN_ARRAY: wordcode = 1;
1912/// `WC_ASSIGN_NEW` constant.
1913pub const WC_ASSIGN_NEW: wordcode = 0;
1914/// `WC_ASSIGN_INC` constant.
1915pub const WC_ASSIGN_INC: wordcode = 1;
1916/// `WC_TIMED_EMPTY` constant.
1917pub const WC_TIMED_EMPTY: wordcode = 0;
1918/// `WC_TIMED_PIPE` constant.
1919pub const WC_TIMED_PIPE: wordcode = 1;
1920/// `WC_FOR_PPARAM` constant.
1921pub const WC_FOR_PPARAM: wordcode = 0;
1922/// `WC_FOR_LIST` constant.
1923pub const WC_FOR_LIST: wordcode = 1;
1924/// `WC_FOR_COND` constant.
1925pub const WC_FOR_COND: wordcode = 2;
1926/// `WC_SELECT_PPARAM` constant.
1927pub const WC_SELECT_PPARAM: wordcode = 0;
1928/// `WC_SELECT_LIST` constant.
1929pub const WC_SELECT_LIST: wordcode = 1;
1930/// `WC_WHILE_WHILE` constant.
1931pub const WC_WHILE_WHILE: wordcode = 0;
1932/// `WC_WHILE_UNTIL` constant.
1933pub const WC_WHILE_UNTIL: wordcode = 1;
1934/// `WC_CASE_HEAD` constant.
1935pub const WC_CASE_HEAD: wordcode = 0;
1936/// `WC_CASE_OR` constant.
1937pub const WC_CASE_OR: wordcode = 1;
1938/// `WC_CASE_AND` constant.
1939pub const WC_CASE_AND: wordcode = 2;
1940/// `WC_CASE_TESTAND` constant.
1941pub const WC_CASE_TESTAND: wordcode = 3;
1942/// `WC_CASE_FREE` constant.
1943pub const WC_CASE_FREE: u32 = 3; // c:1020
1944/// `WC_IF_HEAD` constant.
1945pub const WC_IF_HEAD: wordcode = 0;
1946/// `WC_IF_IF` constant.
1947pub const WC_IF_IF: wordcode = 1;
1948/// `WC_IF_ELIF` constant.
1949pub const WC_IF_ELIF: wordcode = 2;
1950/// `WC_IF_ELSE` constant.
1951pub const WC_IF_ELSE: wordcode = 3;
1952
1953// =============================================================================
1954// 16b. WC accessor + builder macros (zsh.h:918-1038).
1955// Each WC_X_TYPE / WC_X_SKIP / WCB_X is one of the per-opcode
1956// `wc_data` slicers / `wc_bld` constructors.
1957// =============================================================================
1958/// Port of `WCB_END` from `Src/zsh.h:918` — C macro `#define WCB_END() wc_bld(WC_END, 0)`.
1959#[inline]
1960#[allow(non_snake_case)]
1961pub fn WCB_END() -> wordcode {
1962    wc_bld(WC_END, 0)
1963} // c:918
1964/// Port of `WC_LIST_TYPE` from `Src/zsh.h:920` — C macro `#define WC_LIST_TYPE(C) wc_data(C)`.
1965#[inline]
1966#[allow(non_snake_case)]
1967pub fn WC_LIST_TYPE(c: wordcode) -> wordcode {
1968    wc_data(c)
1969} // c:920
1970/// Port of `WC_LIST_SKIP` from `Src/zsh.h:924` — C macro `#define WC_LIST_SKIP(C) (wc_data(C) >> WC_LIST_FREE)`.
1971#[inline]
1972#[allow(non_snake_case)]
1973pub fn WC_LIST_SKIP(c: wordcode) -> wordcode {
1974    wc_data(c) >> WC_LIST_FREE
1975} // c:924
1976/// Port of `WCB_LIST` from `Src/zsh.h:925` — C macro `#define WCB_LIST(T,O) wc_bld(WC_LIST, ((T) | ((O) << WC_LIST_FREE)))`.
1977#[inline]
1978#[allow(non_snake_case)]
1979pub fn WCB_LIST(t: wordcode, o: wordcode) -> wordcode {
1980    wc_bld(WC_LIST, t | (o << WC_LIST_FREE))
1981}
1982/// Port of `WC_SUBLIST_TYPE` from `Src/zsh.h:927` — C macro `#define WC_SUBLIST_TYPE(C) (wc_data(C) & ((wordcode) 3))`.
1983#[inline]
1984#[allow(non_snake_case)]
1985pub fn WC_SUBLIST_TYPE(c: wordcode) -> wordcode {
1986    wc_data(c) & 3
1987} // c:927
1988/// Port of `WC_SUBLIST_FLAGS` from `Src/zsh.h:931` — C macro `#define WC_SUBLIST_FLAGS(C) (wc_data(C) & ((wordcode) 0x1c))`.
1989#[inline]
1990#[allow(non_snake_case)]
1991pub fn WC_SUBLIST_FLAGS(c: wordcode) -> wordcode {
1992    wc_data(c) & 0x1c
1993} // c:931
1994/// Port of `WC_SUBLIST_SKIP` from `Src/zsh.h:936` — C macro `#define WC_SUBLIST_SKIP(C) (wc_data(C) >> WC_SUBLIST_FREE)`.
1995#[inline]
1996#[allow(non_snake_case)]
1997pub fn WC_SUBLIST_SKIP(c: wordcode) -> wordcode {
1998    wc_data(c) >> WC_SUBLIST_FREE
1999}
2000/// Port of `WCB_SUBLIST` from `Src/zsh.h:937` — C macro `#define WCB_SUBLIST(T,F,O) wc_bld(WC_SUBLIST, ((T) | (F) | ((O) << WC_SUBLIST_FREE)))` (c:937-938).
2001#[inline]
2002#[allow(non_snake_case)]
2003pub fn WCB_SUBLIST(t: wordcode, f: wordcode, o: wordcode) -> wordcode {
2004    wc_bld(WC_SUBLIST, t | f | (o << WC_SUBLIST_FREE))
2005}
2006/// Port of `WC_PIPE_TYPE` from `Src/zsh.h:940` — C macro `#define WC_PIPE_TYPE(C) (wc_data(C) & ((wordcode) 1))`.
2007#[inline]
2008#[allow(non_snake_case)]
2009pub fn WC_PIPE_TYPE(c: wordcode) -> wordcode {
2010    wc_data(c) & 1
2011} // c:940
2012/// Port of `WC_PIPE_LINENO` from `Src/zsh.h:943` — C macro `#define WC_PIPE_LINENO(C) (wc_data(C) >> 1)`.
2013#[inline]
2014#[allow(non_snake_case)]
2015pub fn WC_PIPE_LINENO(c: wordcode) -> wordcode {
2016    wc_data(c) >> 1
2017}
2018/// Port of `WCB_PIPE` from `Src/zsh.h:944` — C macro `#define WCB_PIPE(T,L) wc_bld(WC_PIPE, ((T) | ((L) << 1)))`.
2019#[inline]
2020#[allow(non_snake_case)]
2021pub fn WCB_PIPE(t: wordcode, l: wordcode) -> wordcode {
2022    wc_bld(WC_PIPE, t | (l << 1))
2023}
2024/// Port of `WC_REDIR_TYPE` from `Src/zsh.h:946` — C macro `#define WC_REDIR_TYPE(C) ((int)(wc_data(C) & REDIR_TYPE_MASK))`.
2025#[inline]
2026#[allow(non_snake_case)]
2027pub fn WC_REDIR_TYPE(c: wordcode) -> i32 {
2028    (wc_data(c) & REDIR_TYPE_MASK as u32) as i32
2029}
2030/// Port of `WC_REDIR_VARID` from `Src/zsh.h:947` — C macro `#define WC_REDIR_VARID(C) ((int)(wc_data(C) & REDIR_VARID_MASK))`.
2031#[inline]
2032#[allow(non_snake_case)]
2033pub fn WC_REDIR_VARID(c: wordcode) -> i32 {
2034    (wc_data(c) & REDIR_VARID_MASK as u32) as i32
2035}
2036/// Port of `WC_REDIR_FROM_HEREDOC` from `Src/zsh.h:948` — C macro `#define WC_REDIR_FROM_HEREDOC(C) ((int)(wc_data(C) & REDIR_FROM_HEREDOC_MASK))`.
2037#[inline]
2038#[allow(non_snake_case)]
2039pub fn WC_REDIR_FROM_HEREDOC(c: wordcode) -> i32 {
2040    (wc_data(c) & REDIR_FROM_HEREDOC_MASK as u32) as i32
2041}
2042/// Port of `WCB_REDIR` from `Src/zsh.h:949` — C macro `#define WCB_REDIR(T) wc_bld(WC_REDIR, (T))`.
2043#[inline]
2044#[allow(non_snake_case)]
2045pub fn WCB_REDIR(t: wordcode) -> wordcode {
2046    wc_bld(WC_REDIR, t)
2047}
2048/// Port of `WC_REDIR_WORDS` from `Src/zsh.h:951` — C macro `#define WC_REDIR_WORDS(C) ((WC_REDIR_VARID(C) ? 4 : 3) + (WC_REDIR_FROM_HEREDOC(C) ? 2 : 0))` (c:951-953).
2049#[inline]
2050#[allow(non_snake_case)]
2051pub fn WC_REDIR_WORDS(c: wordcode) -> i32 {
2052    (if WC_REDIR_VARID(c) != 0 { 4 } else { 3 })
2053        + (if WC_REDIR_FROM_HEREDOC(c) != 0 { 2 } else { 0 })
2054}
2055/// Port of `WC_ASSIGN_TYPE` from `Src/zsh.h:955` — C macro `#define WC_ASSIGN_TYPE(C) (wc_data(C) & ((wordcode) 1))`.
2056#[inline]
2057#[allow(non_snake_case)]
2058pub fn WC_ASSIGN_TYPE(c: wordcode) -> wordcode {
2059    wc_data(c) & 1
2060} // c:955
2061/// Port of `WC_ASSIGN_TYPE2` from `Src/zsh.h:956` — C macro `#define WC_ASSIGN_TYPE2(C) ((wc_data(C) & ((wordcode) 2)) >> 1)`.
2062#[inline]
2063#[allow(non_snake_case)]
2064pub fn WC_ASSIGN_TYPE2(c: wordcode) -> wordcode {
2065    (wc_data(c) & 2) >> 1
2066}
2067/// Port of `WC_ASSIGN_NUM` from `Src/zsh.h:967` — C macro `#define WC_ASSIGN_NUM(C) (wc_data(C) >> 2)`.
2068#[inline]
2069#[allow(non_snake_case)]
2070pub fn WC_ASSIGN_NUM(c: wordcode) -> wordcode {
2071    wc_data(c) >> 2
2072}
2073/// Port of `WCB_ASSIGN` from `Src/zsh.h:968` — C macro `#define WCB_ASSIGN(T,A,N) wc_bld(WC_ASSIGN, ((T) | ((A) << 1) | ((N) << 2)))`.
2074#[inline]
2075#[allow(non_snake_case)]
2076pub fn WCB_ASSIGN(t: wordcode, a: wordcode, n: wordcode) -> wordcode {
2077    wc_bld(WC_ASSIGN, t | (a << 1) | (n << 2))
2078}
2079/// Port of `WC_SIMPLE_ARGC` from `Src/zsh.h:970` — C macro `#define WC_SIMPLE_ARGC(C) wc_data(C)`.
2080#[inline]
2081#[allow(non_snake_case)]
2082pub fn WC_SIMPLE_ARGC(c: wordcode) -> wordcode {
2083    wc_data(c)
2084} // c:970
2085/// Port of `WCB_SIMPLE` from `Src/zsh.h:971` — C macro `#define WCB_SIMPLE(N) wc_bld(WC_SIMPLE, (N))`.
2086#[inline]
2087#[allow(non_snake_case)]
2088pub fn WCB_SIMPLE(n: wordcode) -> wordcode {
2089    wc_bld(WC_SIMPLE, n)
2090}
2091/// Port of `WC_TYPESET_ARGC` from `Src/zsh.h:973` — C macro `#define WC_TYPESET_ARGC(C) wc_data(C)`.
2092#[inline]
2093#[allow(non_snake_case)]
2094pub fn WC_TYPESET_ARGC(c: wordcode) -> wordcode {
2095    wc_data(c)
2096} // c:973
2097/// Port of `WCB_TYPESET` from `Src/zsh.h:974` — C macro `#define WCB_TYPESET(N) wc_bld(WC_TYPESET, (N))`.
2098#[inline]
2099#[allow(non_snake_case)]
2100pub fn WCB_TYPESET(n: wordcode) -> wordcode {
2101    wc_bld(WC_TYPESET, n)
2102}
2103/// Port of `WC_SUBSH_SKIP` from `Src/zsh.h:976` — C macro `#define WC_SUBSH_SKIP(C) wc_data(C)`.
2104#[inline]
2105#[allow(non_snake_case)]
2106pub fn WC_SUBSH_SKIP(c: wordcode) -> wordcode {
2107    wc_data(c)
2108} // c:976
2109/// Port of `WCB_SUBSH` from `Src/zsh.h:977` — C macro `#define WCB_SUBSH(O) wc_bld(WC_SUBSH, (O))`.
2110#[inline]
2111#[allow(non_snake_case)]
2112pub fn WCB_SUBSH(o: wordcode) -> wordcode {
2113    wc_bld(WC_SUBSH, o)
2114}
2115/// Port of `WC_CURSH_SKIP` from `Src/zsh.h:979` — C macro `#define WC_CURSH_SKIP(C) wc_data(C)`.
2116#[inline]
2117#[allow(non_snake_case)]
2118pub fn WC_CURSH_SKIP(c: wordcode) -> wordcode {
2119    wc_data(c)
2120} // c:979
2121/// Port of `WCB_CURSH` from `Src/zsh.h:980` — C macro `#define WCB_CURSH(O) wc_bld(WC_CURSH, (O))`.
2122#[inline]
2123#[allow(non_snake_case)]
2124pub fn WCB_CURSH(o: wordcode) -> wordcode {
2125    wc_bld(WC_CURSH, o)
2126}
2127/// Port of `WC_TIMED_TYPE` from `Src/zsh.h:982` — C macro `#define WC_TIMED_TYPE(C) wc_data(C)`.
2128#[inline]
2129#[allow(non_snake_case)]
2130pub fn WC_TIMED_TYPE(c: wordcode) -> wordcode {
2131    wc_data(c)
2132} // c:982
2133/// Port of `WCB_TIMED` from `Src/zsh.h:985` — C macro `#define WCB_TIMED(T) wc_bld(WC_TIMED, (T))`.
2134#[inline]
2135#[allow(non_snake_case)]
2136pub fn WCB_TIMED(t: wordcode) -> wordcode {
2137    wc_bld(WC_TIMED, t)
2138}
2139/// Port of `WC_FUNCDEF_SKIP` from `Src/zsh.h:987` — C macro `#define WC_FUNCDEF_SKIP(C) wc_data(C)`.
2140#[inline]
2141#[allow(non_snake_case)]
2142pub fn WC_FUNCDEF_SKIP(c: wordcode) -> wordcode {
2143    wc_data(c)
2144} // c:987
2145/// Port of `WCB_FUNCDEF` from `Src/zsh.h:988` — C macro `#define WCB_FUNCDEF(O) wc_bld(WC_FUNCDEF, (O))`.
2146#[inline]
2147#[allow(non_snake_case)]
2148pub fn WCB_FUNCDEF(o: wordcode) -> wordcode {
2149    wc_bld(WC_FUNCDEF, o)
2150}
2151/// Port of `WC_FOR_TYPE` from `Src/zsh.h:990` — C macro `#define WC_FOR_TYPE(C) (wc_data(C) & 3)`.
2152#[inline]
2153#[allow(non_snake_case)]
2154pub fn WC_FOR_TYPE(c: wordcode) -> wordcode {
2155    wc_data(c) & 3
2156} // c:990
2157/// Port of `WC_FOR_SKIP` from `Src/zsh.h:994` — C macro `#define WC_FOR_SKIP(C) (wc_data(C) >> 2)`.
2158#[inline]
2159#[allow(non_snake_case)]
2160pub fn WC_FOR_SKIP(c: wordcode) -> wordcode {
2161    wc_data(c) >> 2
2162}
2163/// Port of `WCB_FOR` from `Src/zsh.h:995` — C macro `#define WCB_FOR(T,O) wc_bld(WC_FOR, ((T) | ((O) << 2)))`.
2164#[inline]
2165#[allow(non_snake_case)]
2166pub fn WCB_FOR(t: wordcode, o: wordcode) -> wordcode {
2167    wc_bld(WC_FOR, t | (o << 2))
2168}
2169/// Port of `WC_SELECT_TYPE` from `Src/zsh.h:997` — C macro `#define WC_SELECT_TYPE(C) (wc_data(C) & 1)`.
2170#[inline]
2171#[allow(non_snake_case)]
2172pub fn WC_SELECT_TYPE(c: wordcode) -> wordcode {
2173    wc_data(c) & 1
2174} // c:997
2175/// Port of `WC_SELECT_SKIP` from `Src/zsh.h:1000` — C macro `#define WC_SELECT_SKIP(C) (wc_data(C) >> 1)`.
2176#[inline]
2177#[allow(non_snake_case)]
2178pub fn WC_SELECT_SKIP(c: wordcode) -> wordcode {
2179    wc_data(c) >> 1
2180}
2181/// Port of `WCB_SELECT` from `Src/zsh.h:1001` — C macro `#define WCB_SELECT(T,O) wc_bld(WC_SELECT, ((T) | ((O) << 1)))`.
2182#[inline]
2183#[allow(non_snake_case)]
2184pub fn WCB_SELECT(t: wordcode, o: wordcode) -> wordcode {
2185    wc_bld(WC_SELECT, t | (o << 1))
2186}
2187/// Port of `WC_WHILE_TYPE` from `Src/zsh.h:1003` — C macro `#define WC_WHILE_TYPE(C) (wc_data(C) & 1)`.
2188#[inline]
2189#[allow(non_snake_case)]
2190pub fn WC_WHILE_TYPE(c: wordcode) -> wordcode {
2191    wc_data(c) & 1
2192} // c:1003
2193/// Port of `WC_WHILE_SKIP` from `Src/zsh.h:1006` — C macro `#define WC_WHILE_SKIP(C) (wc_data(C) >> 1)`.
2194#[inline]
2195#[allow(non_snake_case)]
2196pub fn WC_WHILE_SKIP(c: wordcode) -> wordcode {
2197    wc_data(c) >> 1
2198}
2199/// Port of `WCB_WHILE` from `Src/zsh.h:1007` — C macro `#define WCB_WHILE(T,O) wc_bld(WC_WHILE, ((T) | ((O) << 1)))`.
2200#[inline]
2201#[allow(non_snake_case)]
2202pub fn WCB_WHILE(t: wordcode, o: wordcode) -> wordcode {
2203    wc_bld(WC_WHILE, t | (o << 1))
2204}
2205/// Port of `WC_REPEAT_SKIP` from `Src/zsh.h:1009` — C macro `#define WC_REPEAT_SKIP(C) wc_data(C)`.
2206#[inline]
2207#[allow(non_snake_case)]
2208pub fn WC_REPEAT_SKIP(c: wordcode) -> wordcode {
2209    wc_data(c)
2210} // c:1009
2211/// Port of `WCB_REPEAT` from `Src/zsh.h:1010` — C macro `#define WCB_REPEAT(O) wc_bld(WC_REPEAT, (O))`.
2212#[inline]
2213#[allow(non_snake_case)]
2214pub fn WCB_REPEAT(o: wordcode) -> wordcode {
2215    wc_bld(WC_REPEAT, o)
2216}
2217/// Port of `WC_TRY_SKIP` from `Src/zsh.h:1012` — C macro `#define WC_TRY_SKIP(C) wc_data(C)`.
2218#[inline]
2219#[allow(non_snake_case)]
2220pub fn WC_TRY_SKIP(c: wordcode) -> wordcode {
2221    wc_data(c)
2222} // c:1012
2223/// Port of `WCB_TRY` from `Src/zsh.h:1013` — C macro `#define WCB_TRY(O) wc_bld(WC_TRY, (O))`.
2224#[inline]
2225#[allow(non_snake_case)]
2226pub fn WCB_TRY(o: wordcode) -> wordcode {
2227    wc_bld(WC_TRY, o)
2228}
2229/// Port of `WC_CASE_TYPE` from `Src/zsh.h:1015` — C macro `#define WC_CASE_TYPE(C) (wc_data(C) & 7)`.
2230#[inline]
2231#[allow(non_snake_case)]
2232pub fn WC_CASE_TYPE(c: wordcode) -> wordcode {
2233    wc_data(c) & 7
2234} // c:1015
2235/// Port of `WC_CASE_SKIP` from `Src/zsh.h:1021` — C macro `#define WC_CASE_SKIP(C) (wc_data(C) >> WC_CASE_FREE)`.
2236#[inline]
2237#[allow(non_snake_case)]
2238pub fn WC_CASE_SKIP(c: wordcode) -> wordcode {
2239    wc_data(c) >> WC_CASE_FREE
2240}
2241/// Port of `WCB_CASE` from `Src/zsh.h:1022` — C macro `#define WCB_CASE(T,O) wc_bld(WC_CASE, ((T) | ((O) << WC_CASE_FREE)))`.
2242#[inline]
2243#[allow(non_snake_case)]
2244pub fn WCB_CASE(t: wordcode, o: wordcode) -> wordcode {
2245    wc_bld(WC_CASE, t | (o << WC_CASE_FREE))
2246}
2247/// Port of `WC_IF_TYPE` from `Src/zsh.h:1024` — C macro `#define WC_IF_TYPE(C) (wc_data(C) & 3)`.
2248#[inline]
2249#[allow(non_snake_case)]
2250pub fn WC_IF_TYPE(c: wordcode) -> wordcode {
2251    wc_data(c) & 3
2252} // c:1024
2253/// Port of `WC_IF_SKIP` from `Src/zsh.h:1029` — C macro `#define WC_IF_SKIP(C) (wc_data(C) >> 2)`.
2254#[inline]
2255#[allow(non_snake_case)]
2256pub fn WC_IF_SKIP(c: wordcode) -> wordcode {
2257    wc_data(c) >> 2
2258}
2259/// Port of `WCB_IF` from `Src/zsh.h:1030` — C macro `#define WCB_IF(T,O) wc_bld(WC_IF, ((T) | ((O) << 2)))`.
2260#[inline]
2261#[allow(non_snake_case)]
2262pub fn WCB_IF(t: wordcode, o: wordcode) -> wordcode {
2263    wc_bld(WC_IF, t | (o << 2))
2264}
2265/// Port of `WC_COND_TYPE` from `Src/zsh.h:1032` — C macro `#define WC_COND_TYPE(C) (wc_data(C) & 127)`.
2266#[inline]
2267#[allow(non_snake_case)]
2268pub fn WC_COND_TYPE(c: wordcode) -> wordcode {
2269    wc_data(c) & 127
2270} // c:1032
2271/// Port of `WC_COND_SKIP` from `Src/zsh.h:1033` — C macro `#define WC_COND_SKIP(C) (wc_data(C) >> 7)`.
2272#[inline]
2273#[allow(non_snake_case)]
2274pub fn WC_COND_SKIP(c: wordcode) -> wordcode {
2275    wc_data(c) >> 7
2276}
2277/// Port of `WCB_COND` from `Src/zsh.h:1034` — C macro `#define WCB_COND(T,O) wc_bld(WC_COND, ((T) | ((O) << 7)))`.
2278#[inline]
2279#[allow(non_snake_case)]
2280pub fn WCB_COND(t: wordcode, o: wordcode) -> wordcode {
2281    wc_bld(WC_COND, t | (o << 7))
2282}
2283/// Port of `WCB_ARITH` from `Src/zsh.h:1036` — C macro `#define WCB_ARITH() wc_bld(WC_ARITH, 0)`.
2284#[inline]
2285#[allow(non_snake_case)]
2286pub fn WCB_ARITH() -> wordcode {
2287    wc_bld(WC_ARITH, 0)
2288} // c:1036
2289/// Port of `WCB_AUTOFN` from `Src/zsh.h:1038` — C macro `#define WCB_AUTOFN() wc_bld(WC_AUTOFN, 0)`.
2290#[inline]
2291#[allow(non_snake_case)]
2292pub fn WCB_AUTOFN() -> wordcode {
2293    wc_bld(WC_AUTOFN, 0)
2294} // c:1038
2295
2296// =============================================================================
2297// 16c. Other macros: BUILTIN/BIN_PREFIX/CONDDEF/HOOKDEF/PARAMDEF/etc.
2298// =============================================================================
2299
2300/// Port of `NULLBINCMD` from `Src/zsh.h:1438` — C macro `#define NULLBINCMD`.
2301pub const NULLBINCMD: Option<HandlerFunc> = None; // c:1438
2302
2303/// Port of `EMULATION` from `Src/zsh.h:2347` — C macro `#define EMULATION(X)`.
2304/// C macro: `(emulation & (X))`. Reads the canonical `emulation`
2305/// static from `crate::ported::options::emulation` directly.
2306#[inline]
2307#[allow(non_snake_case)]
2308pub fn EMULATION(x: i32) -> bool {
2309    // c:2347
2310    let emul = crate::ported::options::emulation.load(std::sync::atomic::Ordering::Relaxed);
2311    (emul & x) != 0
2312}
2313
2314/// Port of `SHELL_EMULATION` from `Src/zsh.h:2350` — C macro `#define SHELL_EMULATION()`.
2315/// C macro: `(emulation & ((1<<5)-1))`. Reads the canonical
2316/// `emulation` static directly.
2317#[inline]
2318#[allow(non_snake_case)]
2319pub fn SHELL_EMULATION() -> i32 {
2320    // c:2350
2321    let emul = crate::ported::options::emulation.load(std::sync::atomic::Ordering::Relaxed);
2322    emul & ((1 << 5) - 1)
2323}
2324
2325/// Port of `IN_EVAL_TRAP` from `Src/zsh.h:2962` — C macro `#define IN_EVAL_TRAP()`.
2326/// C macro reads the four globals `intrap` / `trapisfunc` /
2327/// `traplocallevel` / `locallevel` directly with no args; Rust
2328/// matches by reading the canonical statics
2329/// (`signals::intrap`, `signals::trapisfunc`,
2330/// `signals::traplocallevel`, `params::locallevel`) inside.
2331#[inline]
2332#[allow(non_snake_case)]
2333pub fn IN_EVAL_TRAP() -> bool {
2334    // c:2962
2335    crate::ported::signals::intrap.load(Ordering::Relaxed) != 0
2336        && crate::ported::signals::trapisfunc.load(Ordering::Relaxed) == 0
2337        && crate::ported::signals::traplocallevel.load(Ordering::Relaxed)
2338            == crate::ported::params::locallevel.load(Ordering::Relaxed)
2339}
2340
2341/// Port of `ASG_ARRAYP` from `Src/zsh.h:1288` — C macro `#define ASG_ARRAYP(asg)`.
2342#[inline]
2343#[allow(non_snake_case)]
2344pub fn ASG_ARRAYP(asg: &asgment) -> bool {
2345    (asg.flags & ASG_ARRAY) != 0
2346}
2347
2348/// Port of `ASG_VALUEP` from `Src/zsh.h:1296` — C macro `#define ASG_VALUEP(asg)`.
2349#[inline]
2350#[allow(non_snake_case)]
2351pub fn ASG_VALUEP(asg: &asgment) -> bool {
2352    ASG_ARRAYP(asg) || asg.scalar.is_some()
2353}
2354
2355/// Port of `MB_METASTRLEN2END` from `Src/zsh.h:3282/3363` — C macro
2356/// `#define MB_METASTRLEN2END(str, widthp, eptr)`. C: `mb_metastrlenend(str, widthp, eptr)`
2357/// (multibyte) or `ztrlenend(str, eptr)` (non-multibyte). Rust port
2358/// counts metafied chars from `str` up to `eptr` (exclusive).
2359#[inline]
2360#[allow(non_snake_case)]
2361pub fn MB_METASTRLEN2END(s: &str, widthp: i32, eptr: usize) -> usize {
2362    let truncated = if eptr <= s.len() { &s[..eptr] } else { s };
2363    MB_METASTRLEN2(truncated, widthp)
2364}
2365
2366// Hook-table indices (zsh.h:3259-3262). C: `(zshhooks + N)` —
2367// the Rust port exposes the offsets; consumers index into the
2368// `zshhooks[]` array themselves.
2369/// `EXITHOOK_OFFSET` constant.
2370pub const EXITHOOK_OFFSET: usize = 0; // c:3259
2371/// `BEFORETRAPHOOK_OFFSET` constant.
2372pub const BEFORETRAPHOOK_OFFSET: usize = 1; // c:3260
2373/// `AFTERTRAPHOOK_OFFSET` constant.
2374pub const AFTERTRAPHOOK_OFFSET: usize = 2; // c:3261
2375/// `GETCOLORATTR_OFFSET` constant.
2376pub const GETCOLORATTR_OFFSET: usize = 3; // c:3262
2377
2378/// Port of `STOPHIST` from `Src/zsh.h:2267` — C macro `#define STOPHIST`. Increments the
2379/// `stophist` global by 4. Rust port exposes the delta; the global
2380/// itself lives in `hist.rs`.
2381pub const STOPHIST_DELTA: i32 = 4; // c:2267
2382/// `ALLOWHIST_DELTA` constant.
2383pub const ALLOWHIST_DELTA: i32 = -4; // c:2268
2384
2385/// Aliases under the canonical C macro names. C uses these in
2386/// statement-style: `STOPHIST` and `ALLOWHIST` expand to assignments
2387/// modifying the global; Rust port exposes them as the deltas.
2388pub const STOPHIST: i32 = STOPHIST_DELTA;
2389/// `ALLOWHIST` constant.
2390pub const ALLOWHIST: i32 = ALLOWHIST_DELTA;
2391
2392/// Hook-table indices under their canonical zsh.h names (C: `(zshhooks
2393/// + N)`).
2394pub const EXITHOOK: usize = EXITHOOK_OFFSET;
2395/// `BEFORETRAPHOOK` constant.
2396pub const BEFORETRAPHOOK: usize = BEFORETRAPHOOK_OFFSET;
2397/// `AFTERTRAPHOOK` constant.
2398pub const AFTERTRAPHOOK: usize = AFTERTRAPHOOK_OFFSET;
2399/// `GETCOLORATTR` constant.
2400pub const GETCOLORATTR: usize = GETCOLORATTR_OFFSET;
2401
2402/// Port of `ZLONG_CONST` from `Src/zsh.h:68/72/78/83` — C macro `#define ZLONG_CONST(x)`.
2403/// C casts an integer literal to `zlong` via the `l`/`ll` suffix.
2404/// In Rust integer literals are typed at the use site; this macro
2405/// is an explicit cast to `zlong` (= `i64`).
2406#[inline]
2407#[allow(non_snake_case)]
2408pub const fn ZLONG_CONST(x: i64) -> zlong {
2409    x
2410} // c:68
2411
2412/// Port of `STRINGIFY_LITERAL` from `Src/zsh.h:2915` — C macro `#define STRINGIFY_LITERAL(x)`. C
2413/// uses the `#` operator to stringify an identifier. Rust's
2414/// `stringify!` macro does the same.
2415#[macro_export]
2416macro_rules! STRINGIFY_LITERAL {
2417    ($x:tt) => {
2418        stringify!($x)
2419    };
2420}
2421
2422/// Port of `STRINGIFY` from `Src/zsh.h:2916` — C macro `#define STRINGIFY(x)`. Two-pass
2423/// stringification (expand x first, then stringify).
2424#[macro_export]
2425macro_rules! STRINGIFY {
2426    ($x:tt) => {
2427        $crate::STRINGIFY_LITERAL!($x)
2428    };
2429}
2430
2431/// Port of `ERRMSG` from `Src/zsh.h:2917` — C macro `#define ERRMSG(x)`. Build a debug
2432/// error-message prefix `__FILE__ ":" __LINE__ ": " x`.
2433#[macro_export]
2434macro_rules! ERRMSG {
2435    ($msg:expr) => {
2436        concat!(file!(), ":", line!(), ": ", $msg)
2437    };
2438}
2439
2440/// Port of `HEAPID_FMT` from `Src/zsh.h:2831` — C macro `#define HEAPID_FMT`. printf format
2441/// specifier for `Heapid` values. C uses `"%x"`; Rust uses `"{:x}"`.
2442pub const HEAPID_FMT: &str = "{:x}"; // c:2831
2443
2444/// Port of `HEAP_ERROR` from `Src/zsh.h:2864` — C macro `#define HEAP_ERROR(heap_id)`. Debug-
2445/// only macro that fprintf's an "invalid heap" error to stderr.
2446/// Rust port: eprintln! with the same format. Only active under
2447/// the `zsh-heap-debug` feature.
2448#[macro_export]
2449macro_rules! HEAP_ERROR {
2450    ($heap_id:expr) => {
2451        eprintln!(
2452            "{}:{}: HEAP DEBUG: invalid heap: {:x}.",
2453            file!(),
2454            line!(),
2455            $heap_id
2456        )
2457    };
2458}
2459
2460/// Port of `#define DPUTS(X, Y)` macro from `Src/zsh.h:2918` (macro).
2461///
2462/// C body (DEBUG defined, c:2918):
2463/// ```c
2464/// # define DPUTS(X,Y) if (!(X)) {;} else dputs(ERRMSG(Y))
2465/// ```
2466/// where `ERRMSG(x)` is `(__FILE__ ":" STRINGIFY(__LINE__) ": " x)`
2467/// (c:2917). Without DEBUG (c:2923) the macro expands to nothing.
2468///
2469/// The Rust port routes through `crate::ported::utils::dputs` (port
2470/// of `dputs` at `Src/utils.c:253`) under `#[cfg(feature = "zsh-debug")]`
2471/// — Rust's analogue to C's `#ifdef DEBUG` (set by configure's
2472/// `--enable-zsh-debug`). Stock zsh ships with DEBUG un-set, so the
2473/// default zshrs build (without `--features zsh-debug`) is silent
2474/// too. The file:line prefix uses `file!()` / `line!()` to mirror
2475/// `__FILE__:__LINE__`.
2476#[macro_export]
2477macro_rules! DPUTS {
2478    // c:2918
2479    ($x:expr, $y:expr) => {
2480        // c:2918
2481        #[cfg(feature = "zsh-debug")] // c:2918 ifdef DEBUG
2482        {
2483            if $x {
2484                // c:2918 if (X)
2485                crate::ported::utils::dputs(&format!(
2486                    // c:2918 dputs(ERRMSG(Y))
2487                    "{}:{}: {}",
2488                    file!(),
2489                    line!(),
2490                    $y // c:2917 ERRMSG
2491                )); // c:2918
2492            } // c:2918
2493        } // c:2914 ifdef DEBUG
2494    }; // c:2918
2495} // c:2918
2496
2497/// Port of `#define DPUTS1(X, Y, Z1)` macro from `Src/zsh.h:2919` (macro).
2498///
2499/// C body (DEBUG defined):
2500/// ```c
2501/// # define DPUTS1(X,Y,Z1) if (!(X)) {;} else dputs(ERRMSG(Y), Z1)
2502/// ```
2503/// One-arg printf-style variant — `Y` is a printf format string with
2504/// one `%`-substitution, `Z1` is the argument. The Rust port uses
2505/// `format!` with `{}` placeholders; callers should write Rust-style
2506/// format strings (`"BUG: x = {}"`) instead of C printf strings
2507/// (`"BUG: x = %d"`).
2508#[macro_export]
2509macro_rules! DPUTS1 {
2510    // c:2919
2511    ($x:expr, $y:expr, $z1:expr) => {
2512        // c:2919
2513        #[cfg(feature = "zsh-debug")] // c:2919
2514        {
2515            if $x {
2516                // c:2919
2517                crate::ported::utils::dputs(&format!(
2518                    // c:2919
2519                    "{}:{}: {}",
2520                    file!(),
2521                    line!(),
2522                    format!($y, $z1) // c:2917
2523                )); // c:2919
2524            } // c:2919
2525        } // c:2914
2526    }; // c:2919
2527} // c:2919
2528
2529/// Port of `#define DPUTS2(X, Y, Z1, Z2)` macro from `Src/zsh.h:2920` (macro).
2530///
2531/// Two-arg printf-style variant. Same shape as DPUTS1 but with two
2532/// substitution arguments.
2533#[macro_export]
2534macro_rules! DPUTS2 {
2535    // c:2920
2536    ($x:expr, $y:expr, $z1:expr, $z2:expr) => {
2537        // c:2920
2538        #[cfg(feature = "zsh-debug")] // c:2920
2539        {
2540            if $x {
2541                // c:2920
2542                crate::ported::utils::dputs(&format!(
2543                    // c:2920
2544                    "{}:{}: {}",
2545                    file!(),
2546                    line!(),
2547                    format!($y, $z1, $z2) // c:2917
2548                )); // c:2920
2549            } // c:2920
2550        } // c:2914
2551    }; // c:2920
2552} // c:2920
2553
2554/// Port of `#define DPUTS3(X, Y, Z1, Z2, Z3)` macro from `Src/zsh.h:2921` (macro).
2555///
2556/// Three-arg printf-style variant. Same shape as DPUTS1/DPUTS2 but
2557/// with three substitution arguments.
2558#[macro_export]
2559macro_rules! DPUTS3 {
2560    // c:2921
2561    ($x:expr, $y:expr, $z1:expr, $z2:expr, $z3:expr) => {
2562        // c:2921
2563        #[cfg(feature = "zsh-debug")] // c:2921
2564        {
2565            if $x {
2566                // c:2921
2567                crate::ported::utils::dputs(&format!(
2568                    // c:2921
2569                    "{}:{}: {}",
2570                    file!(),
2571                    line!(),
2572                    format!($y, $z1, $z2, $z3) // c:2917
2573                )); // c:2921
2574            } // c:2921
2575        } // c:2914
2576    }; // c:2921
2577} // c:2921
2578
2579/// Port of `SGTTYFLAG` from `Src/zsh.h:2614/2616` — C macro `#define SGTTYFLAG`. Termios
2580/// flag accessor — `shttyinfo.tio.c_oflag` (HAVE_TERMIOS) or
2581/// `shttyinfo.sgttyb.sg_flags` (sgtty fallback). Rust port exposes
2582/// the field name; consumers access via `&ttyinfo.tio.c_oflag`.
2583pub const SGTTYFLAG_NAME: &str = "tio.c_oflag";
2584
2585/// Canonical alias under the C macro name (consumers reference it
2586/// in error messages / debug output).
2587pub const SGTTYFLAG: &str = SGTTYFLAG_NAME;
2588
2589/// Port of `SGTABTYPE` from `Src/zsh.h:2619/2622/2625` — C macro `#define SGTABTYPE`.
2590/// Tab-expansion mode constant — `TAB3` / `OXTABS` / `XTABS` per
2591/// platform. macOS/BSD use `OXTABS`; Linux uses `XTABS`.
2592#[cfg(target_os = "linux")]
2593pub const SGTABTYPE: u32 = libc::XTABS;
2594/// `SGTABTYPE` constant.
2595#[cfg(not(target_os = "linux"))]
2596pub const SGTABTYPE: u32 = 0;
2597
2598/// Port of `ZWS` from `Src/zsh.h:3329/3373` — C macro `#define ZWS(s)`. Wide-string
2599/// cast. In Rust `&str` is already UTF-8; pass through.
2600#[inline]
2601#[allow(non_snake_case)]
2602pub fn ZWS(s: &str) -> &str {
2603    s
2604}
2605
2606// =============================================================================
2607// 16d. BUILTIN / BIN_PREFIX / CONDDEF / HOOKDEF / NUMMATHFUNC /
2608// STRMATHFUNC / PARAMDEF / INTPARAMDEF / STRPARAMDEF / ARRPARAMDEF /
2609// SPECIALPMDEF / WRAPDEF — table-row builder macros (zsh.h:1450-2125).
2610// These build initialiser literals for the various per-table arrays.
2611// Rust ports as `const fn`-equivalent constructors returning the
2612// matching struct.
2613// =============================================================================
2614
2615/// Port of `BUILTIN` from `Src/zsh.h:1450` — C macro
2616/// `#define BUILTIN(name, flags, handler, min, max, funcid, optstr, defopts)`.
2617#[inline]
2618#[allow(non_snake_case)]
2619pub fn BUILTIN(
2620    name: &str,
2621    flags: i32,
2622    handler: Option<HandlerFunc>,
2623    min: i32,
2624    max: i32,
2625    funcid: i32,
2626    optstr: Option<&str>,
2627    defopts: Option<&str>,
2628) -> builtin {
2629    builtin {
2630        node: hashnode {
2631            next: None,
2632            nam: name.to_string(),
2633            flags,
2634        },
2635        handlerfunc: handler,
2636        minargs: min,
2637        maxargs: max,
2638        funcid,
2639        optstr: optstr.map(|s| s.to_string()),
2640        defopts: defopts.map(|s| s.to_string()),
2641    }
2642}
2643
2644/// Port of `BIN_PREFIX` from `Src/zsh.h:1452` — C macro `BIN_PREFIX(name, flags)`. Builds a
2645/// prefix-builtin entry (no handler, marked with BINF_PREFIX).
2646#[inline]
2647#[allow(non_snake_case)]
2648pub fn BIN_PREFIX(name: &str, flags: i32) -> builtin {
2649    BUILTIN(
2650        name,
2651        flags | BINF_PREFIX as i32,
2652        NULLBINCMD,
2653        0,
2654        0,
2655        0,
2656        None,
2657        None,
2658    )
2659}
2660
2661/// Port of `CONDDEF` from `Src/zsh.h:701` — C macro
2662/// `#define CONDDEF(name, flags, handler, min, max, condid)`.
2663#[inline]
2664#[allow(non_snake_case)]
2665pub fn CONDDEF(
2666    name: &str,
2667    flags: i32,
2668    handler: CondHandler,
2669    min: i32,
2670    max: i32,
2671    condid: i32,
2672) -> conddef {
2673    conddef {
2674        next: None,
2675        name: name.to_string(),
2676        flags,
2677        handler: Some(handler),
2678        min,
2679        max,
2680        condid,
2681        module: None,
2682    }
2683}
2684
2685/// Port of `HOOKDEF` from `Src/zsh.h:1594` — C macro `HOOKDEF(name, func, flags)`:
2686/// `{ NULL, name, (Hookfn) func, flags, NULL }`. `func` accepts `None`
2687/// to match C's `HOOKDEF("exit", NULL, HOOKF_ALL)` form.
2688#[inline]
2689#[allow(non_snake_case)]
2690pub fn HOOKDEF(name: &str, func: Option<Hookfn>, flags: i32) -> hookdef {
2691    hookdef {
2692        next: std::ptr::null_mut(),
2693        name: name.to_string(),
2694        def: func,
2695        flags,
2696        funcs: std::ptr::null_mut(),
2697    }
2698}
2699
2700/// Port of `NUMMATHFUNC` from `Src/zsh.h:133` — C macro `NUMMATHFUNC(name, func, min, max, id)`.
2701#[inline]
2702#[allow(non_snake_case)]
2703pub fn NUMMATHFUNC(name: &str, func: NumMathFunc, min: i32, max: i32, id: i32) -> mathfunc {
2704    mathfunc {
2705        next: None,
2706        name: name.to_string(),
2707        flags: 0,
2708        nfunc: Some(func),
2709        sfunc: None,
2710        module: None,
2711        minargs: min,
2712        maxargs: max,
2713        funcid: id,
2714    }
2715}
2716
2717/// Port of `STRMATHFUNC` from `Src/zsh.h:135` — C macro `STRMATHFUNC(name, func, id)`.
2718#[inline]
2719#[allow(non_snake_case)]
2720pub fn STRMATHFUNC(name: &str, func: StrMathFunc, id: i32) -> mathfunc {
2721    mathfunc {
2722        next: None,
2723        name: name.to_string(),
2724        flags: MFF_STR,
2725        nfunc: None,
2726        sfunc: Some(func),
2727        module: None,
2728        minargs: 0,
2729        maxargs: 0,
2730        funcid: id,
2731    }
2732}
2733
2734/// Port of `PARAMDEF` from `Src/zsh.h:2096` — C macro `PARAMDEF(name, flags, var, gsu)`.
2735#[inline]
2736#[allow(non_snake_case)]
2737pub fn PARAMDEF(name: &str, flags: i32, var: usize, gsu: usize) -> paramdef {
2738    paramdef {
2739        name: name.to_string(),
2740        flags,
2741        var,
2742        gsu,
2743        getnfn: None,
2744        scantfn: None,
2745        pm: None,
2746    }
2747}
2748
2749/// Port of `INTPARAMDEF` from `Src/zsh.h:2105` — C macro `INTPARAMDEF(name, var)`.
2750#[inline]
2751#[allow(non_snake_case)]
2752pub fn INTPARAMDEF(name: &str, var: usize) -> paramdef {
2753    PARAMDEF(name, PM_INTEGER as i32, var, 0)
2754}
2755
2756/// Port of `STRPARAMDEF` from `Src/zsh.h:2107` — C macro `STRPARAMDEF(name, var)`.
2757#[inline]
2758#[allow(non_snake_case)]
2759pub fn STRPARAMDEF(name: &str, var: usize) -> paramdef {
2760    PARAMDEF(name, PM_SCALAR as i32, var, 0)
2761}
2762
2763/// Port of `ARRPARAMDEF` from `Src/zsh.h:2109` — C macro `ARRPARAMDEF(name, var)`.
2764#[inline]
2765#[allow(non_snake_case)]
2766pub fn ARRPARAMDEF(name: &str, var: usize) -> paramdef {
2767    PARAMDEF(name, PM_ARRAY as i32, var, 0)
2768}
2769
2770/// Port of `SPECIALPMDEF` from `Src/zsh.h:2123` — C macro
2771/// `#define SPECIALPMDEF(name, flags, gsufn, getfn, scanfn)`.
2772#[inline]
2773#[allow(non_snake_case)]
2774pub fn SPECIALPMDEF(
2775    name: &str,
2776    flags: i32,
2777    gsufn: usize,
2778    getfn: Option<GetNodeFunc>,
2779    scanfn: Option<ScanTabFunc>,
2780) -> paramdef {
2781    paramdef {
2782        name: name.to_string(),
2783        flags: flags | (PM_SPECIAL | PM_HIDE | PM_HIDEVAL) as i32,
2784        var: 0,
2785        gsu: gsufn,
2786        getnfn: getfn,
2787        scantfn: scanfn,
2788        pm: None,
2789    }
2790}
2791/// Port of `WRAPDEF` from `Src/zsh.h:1371` — C macro `WRAPDEF(func)`.
2792#[inline]
2793#[allow(non_snake_case)]
2794pub fn WRAPDEF(func: WrapFunc) -> funcwrap {
2795    funcwrap {
2796        next: None,
2797        flags: 0,
2798        handler: Some(func),
2799        module: None,
2800    }
2801}
2802
2803// =============================================================================
2804// 17. Job structures (zsh.h:1046-1166).
2805// =============================================================================
2806/// Port of `struct jobfile` from `Src/zsh.h:1046` — a record to be
2807/// deleted or closed at job exit. C is a tagged union
2808/// (`union { char *name; int fd; } u;` + `int is_fd`); the Rust port
2809/// flattens both arms plus the `is_fd` discriminant (only the field
2810/// selected by `is_fd` is valid). `is_fd == 0` → unlink `name`;
2811/// `is_fd == 1` → close `fd`.
2812#[allow(non_camel_case_types)]
2813#[derive(Debug, Clone)]
2814pub struct jobfile {
2815    // c:1046
2816    /// `name` field (`u.name`, valid when `is_fd == 0`).
2817    pub name: Option<String>,
2818    /// `fd` field (`u.fd`, valid when `is_fd == 1`).
2819    pub fd: i32,
2820    /// `is_fd` discriminant.
2821    pub is_fd: i32,
2822}
2823/// `STAT_CHANGED` constant.
2824pub const STAT_CHANGED: i32 = 0x0001; // c:1073
2825/// `STAT_STOPPED` constant.
2826pub const STAT_STOPPED: i32 = 0x0002;
2827/// `STAT_TIMED` constant.
2828pub const STAT_TIMED: i32 = 0x0004;
2829/// `STAT_DONE` constant.
2830pub const STAT_DONE: i32 = 0x0008;
2831/// `STAT_LOCKED` constant.
2832pub const STAT_LOCKED: i32 = 0x0010;
2833/// `STAT_NOPRINT` constant.
2834pub const STAT_NOPRINT: i32 = 0x0020;
2835/// `STAT_INUSE` constant.
2836pub const STAT_INUSE: i32 = 0x0040;
2837/// `STAT_SUPERJOB` constant.
2838pub const STAT_SUPERJOB: i32 = 0x0080;
2839/// `STAT_SUBJOB` constant.
2840pub const STAT_SUBJOB: i32 = 0x0100;
2841/// `STAT_WASSUPER` constant.
2842pub const STAT_WASSUPER: i32 = 0x0200;
2843/// `STAT_CURSH` constant.
2844pub const STAT_CURSH: i32 = 0x0400;
2845/// `STAT_NOSTTY` constant.
2846pub const STAT_NOSTTY: i32 = 0x0800;
2847/// `STAT_ATTACH` constant.
2848pub const STAT_ATTACH: i32 = 0x1000;
2849/// `STAT_SUBLEADER` constant.
2850pub const STAT_SUBLEADER: i32 = 0x2000;
2851/// `STAT_BUILTIN` constant.
2852pub const STAT_BUILTIN: i32 = 0x4000;
2853/// `STAT_SUBJOB_ORPHANED` constant.
2854pub const STAT_SUBJOB_ORPHANED: i32 = 0x8000;
2855/// `STAT_DISOWN` constant.
2856pub const STAT_DISOWN: i32 = 0x10000; // c:1095
2857/// `SP_RUNNING` constant.
2858pub const SP_RUNNING: i32 = -1; // c:1097
2859/// `JOBTEXTSIZE` constant.
2860pub const JOBTEXTSIZE: usize = 80; // c:1104
2861                                   // C: `#define MAXJOBS_ALLOC 50` (Src/zsh.h:1107) — an int literal.
2862                                   // Stored as `usize` so callers using it for Vec capacity / slice
2863                                   // indexing don't need `as usize` casts everywhere. Matches the
2864                                   // adjacent `MAX_PIPESTATS: usize` type choice (both are array
2865                                   // sizes in C).
2866/// `MAXJOBS_ALLOC` constant.
2867pub const MAXJOBS_ALLOC: usize = 50; // c:1107
2868/// `MAX_PIPESTATS` constant.
2869pub const MAX_PIPESTATS: usize = 256; // c:1166
2870/// `timeinfo` — see fields for layout.
2871#[allow(non_camel_case_types)]
2872#[derive(Debug, Clone, Default)]
2873pub struct timeinfo {
2874    // c:1099-1115 — when HAVE_GETRUSAGE the C type is `struct rusage`
2875    // and printtime reads ru_maxrss / ru_majflt / ru_minflt / ru_nswap /
2876    // ru_ixrss / ru_idrss / ru_isrss / ru_inblock / ru_oublock /
2877    // ru_nvcsw / ru_nivcsw / ru_msgsnd / ru_msgrcv / ru_nsignals.
2878    /// `ut` field.
2879    pub ut: i64,
2880    /// `st` field.
2881    pub st: i64,
2882    /// Maximum resident set size (KB).            ru_maxrss (c:945-952)
2883    pub maxrss: i64,
2884    /// Major page faults.                          ru_majflt (c:954-957)
2885    pub majflt: i64,
2886    /// Minor page faults.                          ru_minflt (c:959-962)
2887    pub minflt: i64,
2888    /// Number of swaps.                            ru_nswap  (c:896-899)
2889    pub nswap: i64,
2890    /// Integral shared memory size.                ru_ixrss  (c:901-907)
2891    pub ixrss: i64,
2892    /// Integral unshared data size.                ru_idrss  (c:909-919)
2893    pub idrss: i64,
2894    /// Integral unshared stack size.               ru_isrss
2895    pub isrss: i64,
2896    /// Block input operations.                     ru_inblock
2897    pub inblock: i64,
2898    /// Block output operations.                    ru_oublock
2899    pub oublock: i64,
2900    /// Voluntary context switches.                 ru_nvcsw
2901    pub nvcsw: i64,
2902    /// Involuntary context switches.               ru_nivcsw
2903    pub nivcsw: i64,
2904    /// IPC messages sent.                          ru_msgsnd
2905    pub msgsnd: i64,
2906    /// IPC messages received.                      ru_msgrcv
2907    pub msgrcv: i64,
2908    /// Signals received.                           ru_nsignals
2909    pub nsignals: i64,
2910}
2911
2912impl timeinfo {
2913    /// `user_dur` — see implementation.
2914    pub fn user_dur(&self) -> std::time::Duration {
2915        std::time::Duration::from_micros(self.ut as u64)
2916    }
2917    /// `sys_dur` — see implementation.
2918    pub fn sys_dur(&self) -> std::time::Duration {
2919        std::time::Duration::from_micros(self.st as u64)
2920    }
2921
2922    /// Populate this `timeinfo` from a `libc::rusage` snapshot.
2923    /// On macOS `ru_maxrss` is in bytes; on Linux it's KB — caller
2924    /// normalises via cfg.
2925    #[cfg(unix)]
2926    pub fn from_rusage(r: &libc::rusage) -> Self {
2927        let ut = r.ru_utime.tv_sec as i64 * 1_000_000 + r.ru_utime.tv_usec as i64;
2928        let st = r.ru_stime.tv_sec as i64 * 1_000_000 + r.ru_stime.tv_usec as i64;
2929        #[cfg(target_os = "macos")]
2930        let maxrss = r.ru_maxrss / 1024;
2931        #[cfg(not(target_os = "macos"))]
2932        let maxrss = r.ru_maxrss as i64;
2933        Self {
2934            ut,
2935            st,
2936            maxrss: maxrss as i64,
2937            majflt: r.ru_majflt as i64,
2938            minflt: r.ru_minflt as i64,
2939            nswap: r.ru_nswap as i64,
2940            ixrss: r.ru_ixrss as i64,
2941            idrss: r.ru_idrss as i64,
2942            isrss: r.ru_isrss as i64,
2943            inblock: r.ru_inblock as i64,
2944            oublock: r.ru_oublock as i64,
2945            nvcsw: r.ru_nvcsw as i64,
2946            nivcsw: r.ru_nivcsw as i64,
2947            msgsnd: r.ru_msgsnd as i64,
2948            msgrcv: r.ru_msgrcv as i64,
2949            nsignals: r.ru_nsignals as i64,
2950        }
2951    }
2952}
2953
2954// =============================================================================
2955// 18. Hash table types (zsh.h:1172-1235) — DISABLED.
2956// =============================================================================
2957/// `DISABLED` constant.
2958pub const DISABLED: i32 = 1 << 0; // c:1235
2959
2960// =============================================================================
2961// 19. Alias / asgment / cmdnam / shfunc / funcstack flags + macros.
2962// =============================================================================
2963/// `HASHED` constant.
2964pub const HASHED: i32 = 1 << 1; // c:1312
2965/// `ALIAS_GLOBAL` constant.
2966pub const ALIAS_GLOBAL: i32 = 1 << 1; // c:1261
2967/// `ALIAS_SUFFIX` constant.
2968pub const ALIAS_SUFFIX: i32 = 1 << 2; // c:1263
2969/// `ASG_ARRAY` constant.
2970pub const ASG_ARRAY: i32 = 1; // c:1280
2971/// `ASG_KEY_VALUE` constant.
2972pub const ASG_KEY_VALUE: i32 = 2; // c:1282
2973/// `SFC_NONE` constant.
2974pub const SFC_NONE: i32 = 0; // c:1329
2975/// `SFC_DIRECT` constant.
2976pub const SFC_DIRECT: i32 = 1;
2977/// `SFC_SIGNAL` constant.
2978pub const SFC_SIGNAL: i32 = 2;
2979/// `SFC_HOOK` constant.
2980pub const SFC_HOOK: i32 = 3;
2981/// `SFC_WIDGET` constant.
2982pub const SFC_WIDGET: i32 = 4;
2983/// `SFC_COMPLETE` constant.
2984pub const SFC_COMPLETE: i32 = 5;
2985/// `SFC_CWIDGET` constant.
2986pub const SFC_CWIDGET: i32 = 6;
2987/// `SFC_SUBST` constant.
2988pub const SFC_SUBST: i32 = 7;
2989/// `FS_SOURCE` constant.
2990pub const FS_SOURCE: i32 = 0; // c:1341
2991/// `FS_FUNC` constant.
2992pub const FS_FUNC: i32 = 1;
2993/// `FS_EVAL` constant.
2994pub const FS_EVAL: i32 = 2;
2995/// `WRAPF_ADDED` constant.
2996pub const WRAPF_ADDED: i32 = 1; // c:1369
2997/// `HOOK_SUFFIX` constant.
2998pub const HOOK_SUFFIX: &str = "_functions"; // c:1379
2999/// `HOOK_SUFFIX_LEN` constant.
3000pub const HOOK_SUFFIX_LEN: usize = 11; // c:1381
3001
3002// =============================================================================
3003// 20. Options struct + MAX_OPS + OPT_* macros (zsh.h:1396-1427).
3004// =============================================================================
3005/// `MAX_OPS` constant.
3006pub const MAX_OPS: usize = 128; // c:1396
3007/// `options` — see fields for layout.
3008#[allow(non_camel_case_types)]
3009#[derive(Clone)]
3010pub struct options {
3011    // c:1416
3012    pub ind: [u8; MAX_OPS],
3013    /// `args` field.
3014    pub args: Vec<String>,
3015    /// `argscount` field.
3016    pub argscount: i32,
3017    /// `argsalloc` field.
3018    pub argsalloc: i32,
3019}
3020/// `PARSEARGS_TOPLEVEL` constant.
3021pub const PARSEARGS_TOPLEVEL: i32 = 0x1; // c:1425
3022/// `PARSEARGS_LOGIN` constant.
3023pub const PARSEARGS_LOGIN: i32 = 0x2; // c:1426
3024
3025// Port of OPT_* macros from Src/zsh.h:1400-1414. Each takes
3026// `Options ops` (= `struct options *`) and a char index. The Rust
3027// port takes `&options` (a reference to the struct ported above) and
3028// indexes `ind[c]`. Char indexing is direct (not c-1) per zsh.h:1408
3029// `((ops)->ind[c] != 0)`.
3030
3031/// Port of `OPT_MINUS` from `Src/zsh.h:1400` — C macro `OPT_MINUS(ops,c)` —
3032/// `((ops)->ind[c] & 1)`. True if option was set as `-X`.
3033#[inline]
3034#[allow(non_snake_case)]
3035pub fn OPT_MINUS(ops: &options, c: u8) -> bool {
3036    (ops.ind[c as usize] & 1) != 0
3037}
3038
3039/// Port of `OPT_PLUS` from `Src/zsh.h:1402` — C macro `OPT_PLUS(ops,c)` —
3040/// `((ops)->ind[c] & 2)`. True if option was set as `+X`.
3041#[inline]
3042#[allow(non_snake_case)]
3043pub fn OPT_PLUS(ops: &options, c: u8) -> bool {
3044    (ops.ind[c as usize] & 2) != 0
3045}
3046
3047/// Port of `OPT_ISSET` from `Src/zsh.h:1408` — C macro `OPT_ISSET(ops,c)` —
3048/// `((ops)->ind[c] != 0)`. True if option was set any way.
3049#[inline]
3050#[allow(non_snake_case)]
3051pub fn OPT_ISSET(ops: &options, c: u8) -> bool {
3052    ops.ind[c as usize] != 0
3053}
3054
3055/// Port of `OPT_HASARG` from `Src/zsh.h:1410` — C macro `OPT_HASARG(ops,c)` —
3056/// `((ops)->ind[c] > 3)`. True if option carries an argument.
3057#[inline]
3058#[allow(non_snake_case)]
3059pub fn OPT_HASARG(ops: &options, c: u8) -> bool {
3060    ops.ind[c as usize] > 3
3061}
3062
3063// =============================================================================
3064// 21. Builtin types + BINF_* (zsh.h:1436-1486).
3065// =============================================================================
3066/// `HandlerFunc` type alias.
3067pub type HandlerFunc = fn(name: &str, args: &[String], ops: &options, funcid: i32) -> i32;
3068/// `BINF_PLUSOPTS` constant.
3069pub const BINF_PLUSOPTS: u32 = 1 << 1; // c:1457
3070/// `BINF_PRINTOPTS` constant.
3071pub const BINF_PRINTOPTS: u32 = 1 << 2; // c:1458
3072/// `BINF_ADDED` constant.
3073pub const BINF_ADDED: u32 = 1 << 3; // c:1459
3074/// `BINF_MAGICEQUALS` constant.
3075pub const BINF_MAGICEQUALS: u32 = 1 << 4; // c:1460
3076/// `BINF_PREFIX` constant.
3077pub const BINF_PREFIX: u32 = 1 << 5; // c:1461
3078/// `BINF_DASH` constant.
3079pub const BINF_DASH: u32 = 1 << 6; // c:1462
3080/// `BINF_BUILTIN` constant.
3081pub const BINF_BUILTIN: u32 = 1 << 7; // c:1463
3082/// `BINF_COMMAND` constant.
3083pub const BINF_COMMAND: u32 = 1 << 8; // c:1464
3084/// `BINF_EXEC` constant.
3085pub const BINF_EXEC: u32 = 1 << 9; // c:1465
3086/// `BINF_NOGLOB` constant.
3087pub const BINF_NOGLOB: u32 = 1 << 10; // c:1466
3088/// `BINF_PSPECIAL` constant.
3089pub const BINF_PSPECIAL: u32 = 1 << 11; // c:1467
3090/// `BINF_SKIPINVALID` constant.
3091pub const BINF_SKIPINVALID: u32 = 1 << 12; // c:1469
3092/// `BINF_KEEPNUM` constant.
3093pub const BINF_KEEPNUM: u32 = 1 << 13; // c:1470
3094/// `BINF_SKIPDASH` constant.
3095pub const BINF_SKIPDASH: u32 = 1 << 14; // c:1471
3096/// `BINF_DASHDASHVALID` constant.
3097pub const BINF_DASHDASHVALID: u32 = 1 << 15; // c:1472
3098/// `BINF_CLEARENV` constant.
3099pub const BINF_CLEARENV: u32 = 1 << 16; // c:1473
3100/// `BINF_AUTOALL` constant.
3101pub const BINF_AUTOALL: u32 = 1 << 17; // c:1474
3102/// `BINF_HANDLES_OPTS` constant.
3103pub const BINF_HANDLES_OPTS: u32 = 1 << 18; // c:1480
3104/// `BINF_ASSIGN` constant.
3105pub const BINF_ASSIGN: u32 = 1 << 19; // c:1486
3106
3107// =============================================================================
3108// 22. Module flags (zsh.h:1516-1532).
3109// =============================================================================
3110/// `MOD_BUSY` constant.
3111pub const MOD_BUSY: i32 = 1 << 0; // c:1516
3112/// `MOD_UNLOAD` constant.
3113pub const MOD_UNLOAD: i32 = 1 << 1; // c:1522
3114/// `MOD_SETUP` constant.
3115pub const MOD_SETUP: i32 = 1 << 2; // c:1524
3116/// `MOD_LINKED` constant.
3117pub const MOD_LINKED: i32 = 1 << 3; // c:1526
3118/// `MOD_INIT_S` constant.
3119pub const MOD_INIT_S: i32 = 1 << 4; // c:1528
3120/// `MOD_INIT_B` constant.
3121pub const MOD_INIT_B: i32 = 1 << 5; // c:1530
3122/// `MOD_ALIAS` constant.
3123pub const MOD_ALIAS: i32 = 1 << 6; // c:1532
3124/// `HOOKF_ALL` constant.
3125pub const HOOKF_ALL: i32 = 1; // c:1592
3126
3127// =============================================================================
3128// 23. Pattern flags (zsh.h:1624-1637).
3129// =============================================================================
3130/// `PAT_HEAPDUP` constant.
3131pub const PAT_HEAPDUP: i32 = 0x0000; // c:1624
3132/// `PAT_FILE` constant.
3133pub const PAT_FILE: i32 = 0x0001;
3134/// `PAT_FILET` constant.
3135pub const PAT_FILET: i32 = 0x0002;
3136/// `PAT_ANY` constant.
3137pub const PAT_ANY: i32 = 0x0004;
3138/// `PAT_NOANCH` constant.
3139pub const PAT_NOANCH: i32 = 0x0008;
3140/// `PAT_NOGLD` constant.
3141pub const PAT_NOGLD: i32 = 0x0010;
3142/// `PAT_PURES` constant.
3143pub const PAT_PURES: i32 = 0x0020;
3144/// `PAT_STATIC` constant.
3145pub const PAT_STATIC: i32 = 0x0040;
3146/// `PAT_SCAN` constant.
3147pub const PAT_SCAN: i32 = 0x0080;
3148/// `PAT_ZDUP` constant.
3149pub const PAT_ZDUP: i32 = 0x0100;
3150/// `PAT_NOTSTART` constant.
3151pub const PAT_NOTSTART: i32 = 0x0200;
3152/// `PAT_NOTEND` constant.
3153pub const PAT_NOTEND: i32 = 0x0400;
3154/// `PAT_HAS_EXCLUDP` constant.
3155pub const PAT_HAS_EXCLUDP: i32 = 0x0800;
3156/// `PAT_LCMATCHUC` constant.
3157pub const PAT_LCMATCHUC: i32 = 0x1000;
3158
3159// =============================================================================
3160// 24. zpc_chars enum (zsh.h:1643-1676).
3161// =============================================================================
3162/// `ZPC_SLASH` constant.
3163pub const ZPC_SLASH: i32 = 0;
3164/// `ZPC_NULL` constant.
3165pub const ZPC_NULL: i32 = 1;
3166/// `ZPC_BAR` constant.
3167pub const ZPC_BAR: i32 = 2;
3168/// `ZPC_OUTPAR` constant.
3169pub const ZPC_OUTPAR: i32 = 3;
3170/// `ZPC_TILDE` constant.
3171pub const ZPC_TILDE: i32 = 4;
3172/// `ZPC_SEG_COUNT` constant.
3173pub const ZPC_SEG_COUNT: i32 = 5;
3174/// `ZPC_INPAR` constant.
3175pub const ZPC_INPAR: i32 = ZPC_SEG_COUNT;
3176/// `ZPC_QUEST` constant.
3177pub const ZPC_QUEST: i32 = ZPC_SEG_COUNT + 1;
3178/// `ZPC_STAR` constant.
3179pub const ZPC_STAR: i32 = ZPC_SEG_COUNT + 2;
3180/// `ZPC_INBRACK` constant.
3181pub const ZPC_INBRACK: i32 = ZPC_SEG_COUNT + 3;
3182/// `ZPC_INANG` constant.
3183pub const ZPC_INANG: i32 = ZPC_SEG_COUNT + 4;
3184/// `ZPC_HAT` constant.
3185pub const ZPC_HAT: i32 = ZPC_SEG_COUNT + 5;
3186/// `ZPC_HASH` constant.
3187pub const ZPC_HASH: i32 = ZPC_SEG_COUNT + 6;
3188/// `ZPC_BNULLKEEP` constant.
3189pub const ZPC_BNULLKEEP: i32 = ZPC_SEG_COUNT + 7;
3190/// `ZPC_NO_KSH_GLOB` constant.
3191pub const ZPC_NO_KSH_GLOB: i32 = ZPC_SEG_COUNT + 8;
3192/// `ZPC_KSH_QUEST` constant.
3193pub const ZPC_KSH_QUEST: i32 = ZPC_NO_KSH_GLOB;
3194/// `ZPC_KSH_STAR` constant.
3195pub const ZPC_KSH_STAR: i32 = ZPC_NO_KSH_GLOB + 1;
3196/// `ZPC_KSH_PLUS` constant.
3197pub const ZPC_KSH_PLUS: i32 = ZPC_NO_KSH_GLOB + 2;
3198/// `ZPC_KSH_BANG` constant.
3199pub const ZPC_KSH_BANG: i32 = ZPC_NO_KSH_GLOB + 3;
3200/// `ZPC_KSH_BANG2` constant.
3201pub const ZPC_KSH_BANG2: i32 = ZPC_NO_KSH_GLOB + 4;
3202/// `ZPC_KSH_AT` constant.
3203pub const ZPC_KSH_AT: i32 = ZPC_NO_KSH_GLOB + 5;
3204/// `ZPC_COUNT` constant.
3205pub const ZPC_COUNT: i32 = ZPC_NO_KSH_GLOB + 6;
3206
3207// =============================================================================
3208// 25. PP_* (zsh.h:1707-1735) + GF_* + ZMB_*.
3209// =============================================================================
3210/// `PP_FIRST` constant.
3211pub const PP_FIRST: i32 = 1;
3212/// `PP_ALPHA` constant.
3213pub const PP_ALPHA: i32 = 1;
3214/// `PP_ALNUM` constant.
3215pub const PP_ALNUM: i32 = 2;
3216/// `PP_ASCII` constant.
3217pub const PP_ASCII: i32 = 3;
3218/// `PP_BLANK` constant.
3219pub const PP_BLANK: i32 = 4;
3220/// `PP_CNTRL` constant.
3221pub const PP_CNTRL: i32 = 5;
3222/// `PP_DIGIT` constant.
3223pub const PP_DIGIT: i32 = 6;
3224/// `PP_GRAPH` constant.
3225pub const PP_GRAPH: i32 = 7;
3226/// `PP_LOWER` constant.
3227pub const PP_LOWER: i32 = 8;
3228/// `PP_PRINT` constant.
3229pub const PP_PRINT: i32 = 9;
3230/// `PP_PUNCT` constant.
3231pub const PP_PUNCT: i32 = 10;
3232/// `PP_SPACE` constant.
3233pub const PP_SPACE: i32 = 11;
3234/// `PP_UPPER` constant.
3235pub const PP_UPPER: i32 = 12;
3236/// `PP_XDIGIT` constant.
3237pub const PP_XDIGIT: i32 = 13;
3238/// `PP_IDENT` constant.
3239pub const PP_IDENT: i32 = 14;
3240/// `PP_IFS` constant.
3241pub const PP_IFS: i32 = 15;
3242/// `PP_IFSSPACE` constant.
3243pub const PP_IFSSPACE: i32 = 16;
3244/// `PP_WORD` constant.
3245pub const PP_WORD: i32 = 17;
3246/// `PP_INCOMPLETE` constant.
3247pub const PP_INCOMPLETE: i32 = 18;
3248/// `PP_INVALID` constant.
3249pub const PP_INVALID: i32 = 19;
3250/// `PP_LAST` constant.
3251pub const PP_LAST: i32 = 19;
3252/// `PP_UNKWN` constant.
3253pub const PP_UNKWN: i32 = 20;
3254/// `PP_RANGE` constant.
3255pub const PP_RANGE: i32 = 21;
3256/// `GF_LCMATCHUC` constant.
3257pub const GF_LCMATCHUC: i32 = 0x0100;
3258/// `GF_IGNCASE` constant.
3259pub const GF_IGNCASE: i32 = 0x0200;
3260/// `GF_BACKREF` constant.
3261pub const GF_BACKREF: i32 = 0x0400;
3262/// `GF_MATCHREF` constant.
3263pub const GF_MATCHREF: i32 = 0x0800;
3264/// `GF_MULTIBYTE` constant.
3265pub const GF_MULTIBYTE: i32 = 0x1000;
3266/// `ZMB_VALID` constant.
3267pub const ZMB_VALID: i32 = 0;
3268/// `ZMB_INCOMPLETE` constant.
3269pub const ZMB_INCOMPLETE: i32 = 1;
3270/// `ZMB_INVALID` constant.
3271pub const ZMB_INVALID: i32 = 2;
3272
3273// =============================================================================
3274// 26. Param type flags (zsh.h:1878-1949).
3275// =============================================================================
3276/// `PM_SCALAR` constant.
3277pub const PM_SCALAR: u32 = 0;
3278/// `PM_ARRAY` constant.
3279pub const PM_ARRAY: u32 = 1 << 0;
3280/// `PM_INTEGER` constant.
3281pub const PM_INTEGER: u32 = 1 << 1;
3282/// `PM_EFLOAT` constant.
3283pub const PM_EFLOAT: u32 = 1 << 2;
3284/// `PM_FFLOAT` constant.
3285pub const PM_FFLOAT: u32 = 1 << 3;
3286/// `PM_HASHED` constant.
3287pub const PM_HASHED: u32 = 1 << 4;
3288/// `PM_LEFT` constant.
3289pub const PM_LEFT: u32 = 1 << 5;
3290/// `PM_RIGHT_B` constant.
3291pub const PM_RIGHT_B: u32 = 1 << 6;
3292/// `PM_RIGHT_Z` constant.
3293pub const PM_RIGHT_Z: u32 = 1 << 7;
3294/// `PM_LOWER` constant.
3295pub const PM_LOWER: u32 = 1 << 8;
3296/// `PM_UPPER` constant.
3297pub const PM_UPPER: u32 = 1 << 9;
3298/// `PM_UNDEFINED` constant.
3299pub const PM_UNDEFINED: u32 = 1 << 9;
3300/// `PM_READONLY` constant.
3301pub const PM_READONLY: u32 = 1 << 10;
3302/// `PM_TAGGED` constant.
3303pub const PM_TAGGED: u32 = 1 << 11;
3304/// `PM_EXPORTED` constant.
3305pub const PM_EXPORTED: u32 = 1 << 12;
3306/// `PM_ABSPATH_USED` constant.
3307pub const PM_ABSPATH_USED: u32 = 1 << 12;
3308/// `PM_UNIQUE` constant.
3309pub const PM_UNIQUE: u32 = 1 << 13;
3310/// `PM_UNALIASED` constant.
3311pub const PM_UNALIASED: u32 = 1 << 13;
3312/// `PM_HIDE` constant.
3313pub const PM_HIDE: u32 = 1 << 14;
3314/// `PM_CUR_FPATH` constant.
3315pub const PM_CUR_FPATH: u32 = 1 << 14;
3316/// `PM_HIDEVAL` constant.
3317pub const PM_HIDEVAL: u32 = 1 << 15;
3318/// `PM_WARNNESTED` constant.
3319pub const PM_WARNNESTED: u32 = 1 << 15;
3320/// `PM_TIED` constant.
3321pub const PM_TIED: u32 = 1 << 16;
3322/// `PM_TAGGED_LOCAL` constant.
3323pub const PM_TAGGED_LOCAL: u32 = 1 << 16;
3324/// `PM_DONTIMPORT_SUID` constant.
3325pub const PM_DONTIMPORT_SUID: u32 = 1 << 17;
3326/// `PM_LOADDIR` constant.
3327pub const PM_LOADDIR: u32 = 1 << 17;
3328/// `PM_SINGLE` constant.
3329pub const PM_SINGLE: u32 = 1 << 18;
3330/// `PM_ANONYMOUS` constant.
3331pub const PM_ANONYMOUS: u32 = 1 << 18;
3332/// `PM_LOCAL` constant.
3333pub const PM_LOCAL: u32 = 1 << 19;
3334/// `PM_KSHSTORED` constant.
3335pub const PM_KSHSTORED: u32 = 1 << 19;
3336/// `PM_SPECIAL` constant.
3337pub const PM_SPECIAL: u32 = 1 << 20;
3338/// `PM_ZSHSTORED` constant.
3339pub const PM_ZSHSTORED: u32 = 1 << 20;
3340/// `PM_RO_BY_DESIGN` constant.
3341pub const PM_RO_BY_DESIGN: u32 = 1 << 21;
3342/// `PM_READONLY_SPECIAL` constant.
3343pub const PM_READONLY_SPECIAL: u32 = PM_SPECIAL | PM_READONLY | PM_RO_BY_DESIGN;
3344/// `PM_DONTIMPORT` constant.
3345pub const PM_DONTIMPORT: u32 = 1 << 22;
3346/// `PM_DECLARED` constant.
3347pub const PM_DECLARED: u32 = 1 << 22;
3348/// `PM_RESTRICTED` constant.
3349pub const PM_RESTRICTED: u32 = 1 << 23;
3350/// `PM_UNSET` constant.
3351pub const PM_UNSET: u32 = 1 << 24;
3352/// `PM_DEFAULTED` constant.
3353pub const PM_DEFAULTED: u32 = PM_DECLARED | PM_UNSET;
3354/// `PM_REMOVABLE` constant.
3355pub const PM_REMOVABLE: u32 = 1 << 25;
3356/// `PM_AUTOLOAD` constant.
3357pub const PM_AUTOLOAD: u32 = 1 << 26;
3358/// `PM_NORESTORE` constant.
3359pub const PM_NORESTORE: u32 = 1 << 27;
3360/// `PM_AUTOALL` constant.
3361pub const PM_AUTOALL: u32 = 1 << 27;
3362/// `PM_HASHELEM` constant.
3363pub const PM_HASHELEM: u32 = 1 << 28;
3364/// `PM_NAMEDDIR` constant.
3365pub const PM_NAMEDDIR: u32 = 1 << 29;
3366/// `PM_NAMEREF` constant.
3367pub const PM_NAMEREF: u32 = 1 << 30;
3368/// Port of `PM_TYPE` from `Src/zsh.h:1885` — C macro `#define PM_TYPE(X) (X & (PM_SCALAR|PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_ARRAY|PM_HASHED|PM_NAMEREF))` (c:1885-1886).
3369#[inline]
3370#[allow(non_snake_case)]
3371pub const fn PM_TYPE(x: u32) -> u32 {
3372    x & (PM_SCALAR | PM_INTEGER | PM_EFLOAT | PM_FFLOAT | PM_ARRAY | PM_HASHED | PM_NAMEREF)
3373}
3374/// `TYPESET_OPTSTR` constant.
3375pub const TYPESET_OPTSTR: &str = "aiEFALRZlurtxUhHT"; // c:1947
3376/// `TYPESET_OPTNUM` constant.
3377pub const TYPESET_OPTNUM: &str = "LRZiEF"; // c:1950
3378
3379// =============================================================================
3380// 27. SCANPM_* (zsh.h:1953-1973).
3381// =============================================================================
3382/// `SCANPM_WANTVALS` constant.
3383pub const SCANPM_WANTVALS: u32 = 1 << 0;
3384/// `SCANPM_WANTKEYS` constant.
3385pub const SCANPM_WANTKEYS: u32 = 1 << 1;
3386/// `SCANPM_WANTINDEX` constant.
3387pub const SCANPM_WANTINDEX: u32 = 1 << 2;
3388/// `SCANPM_MATCHKEY` constant.
3389pub const SCANPM_MATCHKEY: u32 = 1 << 3;
3390/// `SCANPM_MATCHVAL` constant.
3391pub const SCANPM_MATCHVAL: u32 = 1 << 4;
3392/// `SCANPM_MATCHMANY` constant.
3393pub const SCANPM_MATCHMANY: u32 = 1 << 5;
3394/// `SCANPM_ASSIGNING` constant.
3395pub const SCANPM_ASSIGNING: u32 = 1 << 6;
3396/// `SCANPM_KEYMATCH` constant.
3397pub const SCANPM_KEYMATCH: u32 = 1 << 7;
3398/// `SCANPM_DQUOTED` constant.
3399pub const SCANPM_DQUOTED: u32 = 1 << 8;
3400/// `SCANPM_ARRONLY` constant.
3401pub const SCANPM_ARRONLY: u32 = 1 << 9;
3402/// `SCANPM_CHECKING` constant.
3403pub const SCANPM_CHECKING: u32 = 1 << 10;
3404/// `SCANPM_NOEXEC` constant.
3405pub const SCANPM_NOEXEC: u32 = 1 << 11;
3406/// `SCANPM_NONAMESPC` constant.
3407pub const SCANPM_NONAMESPC: u32 = 1 << 12;
3408/// `SCANPM_NONAMEREF` constant.
3409pub const SCANPM_NONAMEREF: u32 = 1 << 13;
3410/// `SCANPM_ISVAR_AT` constant.
3411pub const SCANPM_ISVAR_AT: u32 = 1 << 14;
3412
3413// =============================================================================
3414// 28. SUB_* substitution flags (zsh.h:1981-1996).
3415// =============================================================================
3416/// `SUB_END` constant.
3417pub const SUB_END: i32 = 0x0001;
3418/// `SUB_LONG` constant.
3419pub const SUB_LONG: i32 = 0x0002;
3420/// `SUB_SUBSTR` constant.
3421pub const SUB_SUBSTR: i32 = 0x0004;
3422/// `SUB_MATCH` constant.
3423pub const SUB_MATCH: i32 = 0x0008;
3424/// `SUB_REST` constant.
3425pub const SUB_REST: i32 = 0x0010;
3426/// `SUB_BIND` constant.
3427pub const SUB_BIND: i32 = 0x0020;
3428/// `SUB_EIND` constant.
3429pub const SUB_EIND: i32 = 0x0040;
3430/// `SUB_LEN` constant.
3431pub const SUB_LEN: i32 = 0x0080;
3432/// `SUB_ALL` constant.
3433pub const SUB_ALL: i32 = 0x0100;
3434/// `SUB_GLOBAL` constant.
3435pub const SUB_GLOBAL: i32 = 0x0200;
3436/// `SUB_DOSUBST` constant.
3437pub const SUB_DOSUBST: i32 = 0x0400;
3438/// `SUB_RETFAIL` constant.
3439pub const SUB_RETFAIL: i32 = 0x0800;
3440/// `SUB_START` constant.
3441pub const SUB_START: i32 = 0x1000;
3442/// `SUB_LIST` constant.
3443pub const SUB_LIST: i32 = 0x2000;
3444/// `SUB_EGLOB` constant.
3445pub const SUB_EGLOB: i32 = 0x4000;
3446
3447// =============================================================================
3448// 29. ZSHTOK_* + PREFORK_* + MULTSUB_* (zsh.h:2014-2065).
3449// =============================================================================
3450/// `ZSHTOK_SUBST` constant.
3451pub const ZSHTOK_SUBST: i32 = 0x0001;
3452/// `ZSHTOK_SHGLOB` constant.
3453pub const ZSHTOK_SHGLOB: i32 = 0x0002;
3454/// `PREFORK_TYPESET` constant.
3455pub const PREFORK_TYPESET: i32 = 0x01;
3456/// `PREFORK_ASSIGN` constant.
3457pub const PREFORK_ASSIGN: i32 = 0x02;
3458/// `PREFORK_SINGLE` constant.
3459pub const PREFORK_SINGLE: i32 = 0x04;
3460/// `PREFORK_SPLIT` constant.
3461pub const PREFORK_SPLIT: i32 = 0x08;
3462/// `PREFORK_SHWORDSPLIT` constant.
3463pub const PREFORK_SHWORDSPLIT: i32 = 0x10;
3464/// `PREFORK_NOSHWORDSPLIT` constant.
3465pub const PREFORK_NOSHWORDSPLIT: i32 = 0x20;
3466/// `PREFORK_SUBEXP` constant.
3467pub const PREFORK_SUBEXP: i32 = 0x40;
3468/// `PREFORK_KEY_VALUE` constant.
3469pub const PREFORK_KEY_VALUE: i32 = 0x80;
3470/// `PREFORK_NO_UNTOK` constant.
3471pub const PREFORK_NO_UNTOK: i32 = 0x100;
3472/// `MULTSUB_WS_AT_START` constant.
3473pub const MULTSUB_WS_AT_START: i32 = 1;
3474/// `MULTSUB_WS_AT_END` constant.
3475pub const MULTSUB_WS_AT_END: i32 = 2;
3476/// `MULTSUB_PARAM_NAME` constant.
3477pub const MULTSUB_PARAM_NAME: i32 = 4;
3478
3479// =============================================================================
3480// 30. ASSPM_* (zsh.h:2130-2145).
3481// =============================================================================
3482/// `ASSPM_AUGMENT` constant.
3483pub const ASSPM_AUGMENT: i32 = 1 << 0;
3484/// `ASSPM_WARN_CREATE` constant.
3485pub const ASSPM_WARN_CREATE: i32 = 1 << 1;
3486/// `ASSPM_WARN_NESTED` constant.
3487pub const ASSPM_WARN_NESTED: i32 = 1 << 2;
3488/// `ASSPM_WARN` constant.
3489pub const ASSPM_WARN: i32 = ASSPM_WARN_CREATE | ASSPM_WARN_NESTED;
3490/// `ASSPM_ENV_IMPORT` constant.
3491pub const ASSPM_ENV_IMPORT: i32 = 1 << 3;
3492/// `ASSPM_KEY_VALUE` constant.
3493pub const ASSPM_KEY_VALUE: i32 = 1 << 4;
3494
3495// =============================================================================
3496// 31. ND_* + PRINT_* + loop_return + source_return + noerrexit_bits.
3497// =============================================================================
3498/// `ND_USERNAME` constant.
3499pub const ND_USERNAME: i32 = 1 << 1; // c:2157
3500/// `ND_NOABBREV` constant.
3501pub const ND_NOABBREV: i32 = 1 << 2; // c:2158
3502/// `PRINT_NAMEONLY` constant.
3503pub const PRINT_NAMEONLY: i32 = 1 << 0; // c:2179
3504/// `PRINT_TYPE` constant.
3505pub const PRINT_TYPE: i32 = 1 << 1;
3506/// `PRINT_LIST` constant.
3507pub const PRINT_LIST: i32 = 1 << 2;
3508/// `PRINT_KV_PAIR` constant.
3509pub const PRINT_KV_PAIR: i32 = 1 << 3;
3510/// `PRINT_INCLUDEVALUE` constant.
3511pub const PRINT_INCLUDEVALUE: i32 = 1 << 4;
3512/// `PRINT_TYPESET` constant.
3513pub const PRINT_TYPESET: i32 = 1 << 5;
3514/// `PRINT_LINE` constant.
3515pub const PRINT_LINE: i32 = 1 << 6;
3516/// `PRINT_POSIX_EXPORT` constant.
3517pub const PRINT_POSIX_EXPORT: i32 = 1 << 7;
3518/// `PRINT_POSIX_READONLY` constant.
3519pub const PRINT_POSIX_READONLY: i32 = 1 << 8;
3520/// `PRINT_WITH_NAMESPACE` constant.
3521pub const PRINT_WITH_NAMESPACE: i32 = 1 << 9;
3522/// `PRINT_WHENCE_CSH` constant.
3523pub const PRINT_WHENCE_CSH: i32 = 1 << 7; // c:2191
3524/// `PRINT_WHENCE_VERBOSE` constant.
3525pub const PRINT_WHENCE_VERBOSE: i32 = 1 << 8;
3526/// `PRINT_WHENCE_SIMPLE` constant.
3527pub const PRINT_WHENCE_SIMPLE: i32 = 1 << 9;
3528/// `PRINT_WHENCE_FUNCDEF` constant.
3529pub const PRINT_WHENCE_FUNCDEF: i32 = 1 << 10;
3530/// `PRINT_WHENCE_WORD` constant.
3531pub const PRINT_WHENCE_WORD: i32 = 1 << 11;
3532/// `LOOP_OK` constant.
3533pub const LOOP_OK: i32 = 0; // c:2199
3534/// `LOOP_EMPTY` constant.
3535pub const LOOP_EMPTY: i32 = 1;
3536/// `LOOP_ERROR` constant.
3537pub const LOOP_ERROR: i32 = 2;
3538/// `SOURCE_OK` constant.
3539pub const SOURCE_OK: i32 = 0; // c:2210
3540/// `SOURCE_NOT_FOUND` constant.
3541pub const SOURCE_NOT_FOUND: i32 = 1;
3542/// `SOURCE_ERROR` constant.
3543pub const SOURCE_ERROR: i32 = 2;
3544/// `NOERREXIT_EXIT` constant.
3545pub const NOERREXIT_EXIT: i32 = 1; // c:2219
3546/// `NOERREXIT_RETURN` constant.
3547pub const NOERREXIT_RETURN: i32 = 2;
3548/// `NOERREXIT_SIGNAL` constant.
3549pub const NOERREXIT_SIGNAL: i32 = 8;
3550
3551// =============================================================================
3552// 32. History flags + GETHIST_* + HISTFLAG_* + HFILE_* + LEXFLAGS_*.
3553// =============================================================================
3554/// `HIST_MAKEUNIQUE` constant.
3555pub const HIST_MAKEUNIQUE: u32 = 0x00000001; // c:2252
3556/// `HIST_OLD` constant.
3557pub const HIST_OLD: u32 = 0x00000002;
3558/// `HIST_READ` constant.
3559pub const HIST_READ: u32 = 0x00000004;
3560/// `HIST_DUP` constant.
3561pub const HIST_DUP: u32 = 0x00000008;
3562/// `HIST_FOREIGN` constant.
3563pub const HIST_FOREIGN: u32 = 0x00000010;
3564/// `HIST_TMPSTORE` constant.
3565pub const HIST_TMPSTORE: u32 = 0x00000020;
3566/// `HIST_NOWRITE` constant.
3567pub const HIST_NOWRITE: u32 = 0x00000040;
3568/// `GETHIST_UPWARD` constant.
3569pub const GETHIST_UPWARD: i32 = -1;
3570/// `GETHIST_DOWNWARD` constant.
3571pub const GETHIST_DOWNWARD: i32 = 1;
3572/// `GETHIST_EXACT` constant.
3573pub const GETHIST_EXACT: i32 = 0;
3574/// `HISTFLAG_DONE` constant.
3575pub const HISTFLAG_DONE: i32 = 1; // c:2270
3576/// `HISTFLAG_NOEXEC` constant.
3577pub const HISTFLAG_NOEXEC: i32 = 2;
3578/// `HISTFLAG_RECALL` constant.
3579pub const HISTFLAG_RECALL: i32 = 4;
3580/// `HISTFLAG_SETTY` constant.
3581pub const HISTFLAG_SETTY: i32 = 8;
3582/// `HFILE_APPEND` constant.
3583pub const HFILE_APPEND: u32 = 0x0001;
3584/// `HFILE_SKIPOLD` constant.
3585pub const HFILE_SKIPOLD: u32 = 0x0002;
3586/// `HFILE_SKIPDUPS` constant.
3587pub const HFILE_SKIPDUPS: u32 = 0x0004;
3588/// `HFILE_SKIPFOREIGN` constant.
3589pub const HFILE_SKIPFOREIGN: u32 = 0x0008;
3590/// `HFILE_FAST` constant.
3591pub const HFILE_FAST: u32 = 0x0010;
3592/// `HFILE_NO_REWRITE` constant.
3593pub const HFILE_NO_REWRITE: u32 = 0x0020;
3594/// `HFILE_USE_OPTIONS` constant.
3595pub const HFILE_USE_OPTIONS: u32 = 0x8000;
3596/// `LEXFLAGS_ACTIVE` constant.
3597pub const LEXFLAGS_ACTIVE: i32 = 0x0001;
3598/// `LEXFLAGS_ZLE` constant.
3599pub const LEXFLAGS_ZLE: i32 = 0x0002;
3600/// `LEXFLAGS_COMMENTS_KEEP` constant.
3601pub const LEXFLAGS_COMMENTS_KEEP: i32 = 0x0004;
3602/// `LEXFLAGS_COMMENTS_STRIP` constant.
3603pub const LEXFLAGS_COMMENTS_STRIP: i32 = 0x0008;
3604/// `LEXFLAGS_COMMENTS` constant.
3605pub const LEXFLAGS_COMMENTS: i32 = LEXFLAGS_COMMENTS_KEEP | LEXFLAGS_COMMENTS_STRIP;
3606/// `LEXFLAGS_NEWLINE` constant.
3607pub const LEXFLAGS_NEWLINE: i32 = 0x0010;
3608
3609// =============================================================================
3610// 33. Completion context (zsh.h:2322-2332).
3611// =============================================================================
3612/// `IN_NOTHING` constant.
3613pub const IN_NOTHING: i32 = 0;
3614/// `IN_CMD` constant.
3615pub const IN_CMD: i32 = 1;
3616/// `IN_MATH` constant.
3617pub const IN_MATH: i32 = 2;
3618/// `IN_COND` constant.
3619pub const IN_COND: i32 = 3;
3620/// `IN_ENV` constant.
3621pub const IN_ENV: i32 = 4;
3622/// `IN_PAR` constant.
3623pub const IN_PAR: i32 = 5;
3624
3625// =============================================================================
3626// 34. Emulation flags (zsh.h:2341-2358).
3627// =============================================================================
3628/// `EMULATE_CSH` constant.
3629pub const EMULATE_CSH: i32 = 1 << 1; // c:2341
3630/// `EMULATE_KSH` constant.
3631pub const EMULATE_KSH: i32 = 1 << 2;
3632/// `EMULATE_SH` constant.
3633pub const EMULATE_SH: i32 = 1 << 3;
3634/// `EMULATE_ZSH` constant.
3635pub const EMULATE_ZSH: i32 = 1 << 4;
3636/// `EMULATE_FULLY` constant.
3637pub const EMULATE_FULLY: i32 = 1 << 5;
3638/// `EMULATE_UNUSED` constant.
3639pub const EMULATE_UNUSED: i32 = 1 << 6;
3640
3641// =============================================================================
3642// 35. Option indices (zsh.h:2362-2550).
3643// =============================================================================
3644/// `OPT_INVALID` constant.
3645pub const OPT_INVALID: i32 = 0;
3646/// `ALIASESOPT` constant.
3647pub const ALIASESOPT: i32 = 1;
3648/// `ALIASFUNCDEF` constant.
3649pub const ALIASFUNCDEF: i32 = 2;
3650/// `ALLEXPORT` constant.
3651pub const ALLEXPORT: i32 = 3;
3652/// `ALWAYSLASTPROMPT` constant.
3653pub const ALWAYSLASTPROMPT: i32 = 4;
3654/// `ALWAYSTOEND` constant.
3655pub const ALWAYSTOEND: i32 = 5;
3656/// `APPENDHISTORY` constant.
3657pub const APPENDHISTORY: i32 = 6;
3658/// `AUTOCD` constant.
3659pub const AUTOCD: i32 = 7;
3660/// `AUTOCONTINUE` constant.
3661pub const AUTOCONTINUE: i32 = 8;
3662/// `AUTOLIST` constant.
3663pub const AUTOLIST: i32 = 9;
3664/// `AUTOMENU` constant.
3665pub const AUTOMENU: i32 = 10;
3666/// `AUTONAMEDIRS` constant.
3667pub const AUTONAMEDIRS: i32 = 11;
3668/// `AUTOPARAMKEYS` constant.
3669pub const AUTOPARAMKEYS: i32 = 12;
3670/// `AUTOPARAMSLASH` constant.
3671pub const AUTOPARAMSLASH: i32 = 13;
3672/// `AUTOPUSHD` constant.
3673pub const AUTOPUSHD: i32 = 14;
3674/// `AUTOREMOVESLASH` constant.
3675pub const AUTOREMOVESLASH: i32 = 15;
3676/// `AUTORESUME` constant.
3677pub const AUTORESUME: i32 = 16;
3678/// `BADPATTERN` constant.
3679pub const BADPATTERN: i32 = 17;
3680/// `BANGHIST` constant.
3681pub const BANGHIST: i32 = 18;
3682/// `BAREGLOBQUAL` constant.
3683pub const BAREGLOBQUAL: i32 = 19;
3684/// `BASHAUTOLIST` constant.
3685pub const BASHAUTOLIST: i32 = 20;
3686/// `BASHREMATCH` constant.
3687pub const BASHREMATCH: i32 = 21;
3688/// `BEEP` constant.
3689pub const BEEP: i32 = 22;
3690/// `BGNICE` constant.
3691pub const BGNICE: i32 = 23;
3692/// `BRACECCL` constant.
3693pub const BRACECCL: i32 = 24;
3694/// `BSDECHO` constant.
3695pub const BSDECHO: i32 = 25;
3696/// `CASEGLOB` constant.
3697pub const CASEGLOB: i32 = 26;
3698/// `CASEMATCH` constant.
3699pub const CASEMATCH: i32 = 27;
3700/// `CASEPATHS` constant.
3701pub const CASEPATHS: i32 = 28;
3702/// `CBASES` constant.
3703pub const CBASES: i32 = 29;
3704/// `CDABLEVARS` constant.
3705pub const CDABLEVARS: i32 = 30;
3706/// `CDSILENT` constant.
3707pub const CDSILENT: i32 = 31;
3708/// `CHASEDOTS` constant.
3709pub const CHASEDOTS: i32 = 32;
3710/// `CHASELINKS` constant.
3711pub const CHASELINKS: i32 = 33;
3712/// `CHECKJOBS` constant.
3713pub const CHECKJOBS: i32 = 34;
3714/// `CHECKRUNNINGJOBS` constant.
3715pub const CHECKRUNNINGJOBS: i32 = 35;
3716/// `CLOBBER` constant.
3717pub const CLOBBER: i32 = 36;
3718/// `CLOBBEREMPTY` constant.
3719pub const CLOBBEREMPTY: i32 = 37;
3720/// `APPENDCREATE` constant.
3721pub const APPENDCREATE: i32 = 38;
3722/// `COMBININGCHARS` constant.
3723pub const COMBININGCHARS: i32 = 39;
3724/// `COMPLETEALIASES` constant.
3725pub const COMPLETEALIASES: i32 = 40;
3726/// `COMPLETEINWORD` constant.
3727pub const COMPLETEINWORD: i32 = 41;
3728/// `CORRECT` constant.
3729pub const CORRECT: i32 = 42;
3730/// `CORRECTALL` constant.
3731pub const CORRECTALL: i32 = 43;
3732/// `CONTINUEONERROR` constant.
3733pub const CONTINUEONERROR: i32 = 44;
3734/// `CPRECEDENCES` constant.
3735pub const CPRECEDENCES: i32 = 45;
3736/// `CSHJUNKIEHISTORY` constant.
3737pub const CSHJUNKIEHISTORY: i32 = 46;
3738/// `CSHJUNKIELOOPS` constant.
3739pub const CSHJUNKIELOOPS: i32 = 47;
3740/// `CSHJUNKIEQUOTES` constant.
3741pub const CSHJUNKIEQUOTES: i32 = 48;
3742/// `CSHNULLCMD` constant.
3743pub const CSHNULLCMD: i32 = 49;
3744/// `CSHNULLGLOB` constant.
3745pub const CSHNULLGLOB: i32 = 50;
3746/// `DEBUGBEFORECMD` constant.
3747pub const DEBUGBEFORECMD: i32 = 51;
3748/// `EMACSMODE` constant.
3749pub const EMACSMODE: i32 = 52;
3750/// `EQUALSOPT` constant.
3751pub const EQUALSOPT: i32 = 53; // C name "EQUALS" collides with our token const
3752/// `ERREXIT` constant.
3753pub const ERREXIT: i32 = 54;
3754/// `ERRRETURN` constant.
3755pub const ERRRETURN: i32 = 55;
3756/// `EXECOPT` constant.
3757pub const EXECOPT: i32 = 56;
3758/// `EXTENDEDGLOB` constant.
3759pub const EXTENDEDGLOB: i32 = 57;
3760/// `EXTENDEDHISTORY` constant.
3761pub const EXTENDEDHISTORY: i32 = 58;
3762/// `EVALLINENO` constant.
3763pub const EVALLINENO: i32 = 59;
3764/// `FLOWCONTROL` constant.
3765pub const FLOWCONTROL: i32 = 60;
3766/// `FORCEFLOAT` constant.
3767pub const FORCEFLOAT: i32 = 61;
3768/// `FUNCTIONARGZERO` constant.
3769pub const FUNCTIONARGZERO: i32 = 62;
3770/// `GLOBOPT` constant.
3771pub const GLOBOPT: i32 = 63;
3772/// `GLOBALEXPORT` constant.
3773pub const GLOBALEXPORT: i32 = 64;
3774/// `GLOBALRCS` constant.
3775pub const GLOBALRCS: i32 = 65;
3776/// `GLOBASSIGN` constant.
3777pub const GLOBASSIGN: i32 = 66;
3778/// `GLOBCOMPLETE` constant.
3779pub const GLOBCOMPLETE: i32 = 67;
3780/// `GLOBDOTS` constant.
3781pub const GLOBDOTS: i32 = 68;
3782/// `GLOBSTARSHORT` constant.
3783pub const GLOBSTARSHORT: i32 = 69;
3784/// `GLOBSUBST` constant.
3785pub const GLOBSUBST: i32 = 70;
3786/// `HASHCMDS` constant.
3787pub const HASHCMDS: i32 = 71;
3788/// `HASHDIRS` constant.
3789pub const HASHDIRS: i32 = 72;
3790/// `HASHEXECUTABLESONLY` constant.
3791pub const HASHEXECUTABLESONLY: i32 = 73;
3792/// `HASHLISTALL` constant.
3793pub const HASHLISTALL: i32 = 74;
3794/// `HISTALLOWCLOBBER` constant.
3795pub const HISTALLOWCLOBBER: i32 = 75;
3796/// `HISTBEEP` constant.
3797pub const HISTBEEP: i32 = 76;
3798/// `HISTEXPIREDUPSFIRST` constant.
3799pub const HISTEXPIREDUPSFIRST: i32 = 77;
3800/// `HISTFCNTLLOCK` constant.
3801pub const HISTFCNTLLOCK: i32 = 78;
3802/// `HISTFINDNODUPS` constant.
3803pub const HISTFINDNODUPS: i32 = 79;
3804/// `HISTIGNOREALLDUPS` constant.
3805pub const HISTIGNOREALLDUPS: i32 = 80;
3806/// `HISTIGNOREDUPS` constant.
3807pub const HISTIGNOREDUPS: i32 = 81;
3808/// `HISTIGNORESPACE` constant.
3809pub const HISTIGNORESPACE: i32 = 82;
3810/// `HISTLEXWORDS` constant.
3811pub const HISTLEXWORDS: i32 = 83;
3812/// `HISTNOFUNCTIONS` constant.
3813pub const HISTNOFUNCTIONS: i32 = 84;
3814/// `HISTNOSTORE` constant.
3815pub const HISTNOSTORE: i32 = 85;
3816/// `HISTREDUCEBLANKS` constant.
3817pub const HISTREDUCEBLANKS: i32 = 86;
3818/// `HISTSAVEBYCOPY` constant.
3819pub const HISTSAVEBYCOPY: i32 = 87;
3820/// `HISTSAVENODUPS` constant.
3821pub const HISTSAVENODUPS: i32 = 88;
3822/// `HISTSUBSTPATTERN` constant.
3823pub const HISTSUBSTPATTERN: i32 = 89;
3824/// `HISTVERIFY` constant.
3825pub const HISTVERIFY: i32 = 90;
3826/// `HUP` constant.
3827pub const HUP: i32 = 91;
3828/// `IGNOREBRACES` constant.
3829pub const IGNOREBRACES: i32 = 92;
3830/// `IGNORECLOSEBRACES` constant.
3831pub const IGNORECLOSEBRACES: i32 = 93;
3832/// `IGNOREEOF` constant.
3833pub const IGNOREEOF: i32 = 94;
3834/// `INCAPPENDHISTORY` constant.
3835pub const INCAPPENDHISTORY: i32 = 95;
3836/// `INCAPPENDHISTORYTIME` constant.
3837pub const INCAPPENDHISTORYTIME: i32 = 96;
3838/// `INTERACTIVE` constant.
3839pub const INTERACTIVE: i32 = 97;
3840/// `INTERACTIVECOMMENTS` constant.
3841pub const INTERACTIVECOMMENTS: i32 = 98;
3842/// `KSHARRAYS` constant.
3843pub const KSHARRAYS: i32 = 99;
3844/// `KSHAUTOLOAD` constant.
3845pub const KSHAUTOLOAD: i32 = 100;
3846/// `KSHGLOB` constant.
3847pub const KSHGLOB: i32 = 101;
3848/// `KSHOPTIONPRINT` constant.
3849pub const KSHOPTIONPRINT: i32 = 102;
3850/// `KSHTYPESET` constant.
3851pub const KSHTYPESET: i32 = 103;
3852/// `KSHZEROSUBSCRIPT` constant.
3853pub const KSHZEROSUBSCRIPT: i32 = 104;
3854/// `LISTAMBIGUOUS` constant.
3855pub const LISTAMBIGUOUS: i32 = 105;
3856/// `LISTBEEP` constant.
3857pub const LISTBEEP: i32 = 106;
3858/// `LISTPACKED` constant.
3859pub const LISTPACKED: i32 = 107;
3860/// `LISTROWSFIRST` constant.
3861pub const LISTROWSFIRST: i32 = 108;
3862/// `LISTTYPES` constant.
3863pub const LISTTYPES: i32 = 109;
3864/// `LOCALLOOPS` constant.
3865pub const LOCALLOOPS: i32 = 110;
3866/// `LOCALOPTIONS` constant.
3867pub const LOCALOPTIONS: i32 = 111;
3868/// `LOCALPATTERNS` constant.
3869pub const LOCALPATTERNS: i32 = 112;
3870/// `LOCALTRAPS` constant.
3871pub const LOCALTRAPS: i32 = 113;
3872/// `LOGINSHELL` constant.
3873pub const LOGINSHELL: i32 = 114;
3874/// `LONGLISTJOBS` constant.
3875pub const LONGLISTJOBS: i32 = 115;
3876/// `MAGICEQUALSUBST` constant.
3877pub const MAGICEQUALSUBST: i32 = 116;
3878/// `MAILWARNING` constant.
3879pub const MAILWARNING: i32 = 117;
3880/// `MARKDIRS` constant.
3881pub const MARKDIRS: i32 = 118;
3882/// `MENUCOMPLETE` constant.
3883pub const MENUCOMPLETE: i32 = 119;
3884/// `MONITOR` constant.
3885pub const MONITOR: i32 = 120;
3886/// `MULTIBYTE` constant.
3887pub const MULTIBYTE: i32 = 121;
3888/// `MULTIFUNCDEF` constant.
3889pub const MULTIFUNCDEF: i32 = 122;
3890/// `MULTIOS` constant.
3891pub const MULTIOS: i32 = 123;
3892/// `NOMATCH` constant.
3893pub const NOMATCH: i32 = 124;
3894/// `NOTIFY` constant.
3895pub const NOTIFY: i32 = 125;
3896/// `NULLGLOB` constant.
3897pub const NULLGLOB: i32 = 126;
3898/// `NUMERICGLOBSORT` constant.
3899pub const NUMERICGLOBSORT: i32 = 127;
3900/// `OCTALZEROES` constant.
3901pub const OCTALZEROES: i32 = 128;
3902/// `OVERSTRIKE` constant.
3903pub const OVERSTRIKE: i32 = 129;
3904/// `PATHDIRS` constant.
3905pub const PATHDIRS: i32 = 130;
3906/// `PATHSCRIPT` constant.
3907pub const PATHSCRIPT: i32 = 131;
3908/// `PIPEFAIL` constant.
3909pub const PIPEFAIL: i32 = 132;
3910/// `POSIXALIASES` constant.
3911pub const POSIXALIASES: i32 = 133;
3912/// `POSIXARGZERO` constant.
3913pub const POSIXARGZERO: i32 = 134;
3914/// `POSIXBUILTINS` constant.
3915pub const POSIXBUILTINS: i32 = 135;
3916/// `POSIXCD` constant.
3917pub const POSIXCD: i32 = 136;
3918/// `POSIXIDENTIFIERS` constant.
3919pub const POSIXIDENTIFIERS: i32 = 137;
3920/// `POSIXJOBS` constant.
3921pub const POSIXJOBS: i32 = 138;
3922/// `POSIXSTRINGS` constant.
3923pub const POSIXSTRINGS: i32 = 139;
3924/// `POSIXTRAPS` constant.
3925pub const POSIXTRAPS: i32 = 140;
3926/// `PRINTEIGHTBIT` constant.
3927pub const PRINTEIGHTBIT: i32 = 141;
3928/// `PRINTEXITVALUE` constant.
3929pub const PRINTEXITVALUE: i32 = 142;
3930/// `PRIVILEGED` constant.
3931pub const PRIVILEGED: i32 = 143;
3932/// `PROMPTBANG` constant.
3933pub const PROMPTBANG: i32 = 144;
3934/// `PROMPTCR` constant.
3935pub const PROMPTCR: i32 = 145;
3936/// `PROMPTPERCENT` constant.
3937pub const PROMPTPERCENT: i32 = 146;
3938/// `PROMPTSP` constant.
3939pub const PROMPTSP: i32 = 147;
3940/// `PROMPTSUBST` constant.
3941pub const PROMPTSUBST: i32 = 148;
3942/// `PUSHDIGNOREDUPS` constant.
3943pub const PUSHDIGNOREDUPS: i32 = 149;
3944/// `PUSHDMINUS` constant.
3945pub const PUSHDMINUS: i32 = 150;
3946/// `PUSHDSILENT` constant.
3947pub const PUSHDSILENT: i32 = 151;
3948/// `PUSHDTOHOME` constant.
3949pub const PUSHDTOHOME: i32 = 152;
3950/// `RCEXPANDPARAM` constant.
3951pub const RCEXPANDPARAM: i32 = 153;
3952/// `RCQUOTES` constant.
3953pub const RCQUOTES: i32 = 154;
3954/// `RCS` constant.
3955pub const RCS: i32 = 155;
3956/// `RECEXACT` constant.
3957pub const RECEXACT: i32 = 156;
3958/// `REMATCHPCRE` constant.
3959pub const REMATCHPCRE: i32 = 157;
3960/// `RMSTARSILENT` constant.
3961pub const RMSTARSILENT: i32 = 158;
3962/// `RMSTARWAIT` constant.
3963pub const RMSTARWAIT: i32 = 159;
3964/// `SHAREHISTORY` constant.
3965pub const SHAREHISTORY: i32 = 160;
3966/// `SHFILEEXPANSION` constant.
3967pub const SHFILEEXPANSION: i32 = 161;
3968/// `SHGLOB` constant.
3969pub const SHGLOB: i32 = 162;
3970/// `SHINSTDIN` constant.
3971pub const SHINSTDIN: i32 = 163;
3972/// `SHNULLCMD` constant.
3973pub const SHNULLCMD: i32 = 164;
3974/// `SHOPTIONLETTERS` constant.
3975pub const SHOPTIONLETTERS: i32 = 165;
3976/// `SHORTLOOPS` constant.
3977pub const SHORTLOOPS: i32 = 166;
3978/// `SHORTREPEAT` constant.
3979pub const SHORTREPEAT: i32 = 167;
3980/// `SHWORDSPLIT` constant.
3981pub const SHWORDSPLIT: i32 = 168;
3982/// `SINGLECOMMAND` constant.
3983pub const SINGLECOMMAND: i32 = 169;
3984/// `SINGLELINEZLE` constant.
3985pub const SINGLELINEZLE: i32 = 170;
3986/// `SOURCETRACE` constant.
3987pub const SOURCETRACE: i32 = 171;
3988/// `SUNKEYBOARDHACK` constant.
3989pub const SUNKEYBOARDHACK: i32 = 172;
3990/// `TRANSIENTRPROMPT` constant.
3991pub const TRANSIENTRPROMPT: i32 = 173;
3992/// `TRAPSASYNC` constant.
3993pub const TRAPSASYNC: i32 = 174;
3994/// `TYPESETSILENT` constant.
3995pub const TYPESETSILENT: i32 = 175;
3996/// `TYPESETTOUNSET` constant.
3997pub const TYPESETTOUNSET: i32 = 176;
3998/// `UNSET` constant.
3999pub const UNSET: i32 = 177;
4000/// `VERBOSE` constant.
4001pub const VERBOSE: i32 = 178;
4002/// `VIMODE` constant.
4003pub const VIMODE: i32 = 179;
4004/// `WARNCREATEGLOBAL` constant.
4005pub const WARNCREATEGLOBAL: i32 = 180;
4006/// `WARNNESTEDVAR` constant.
4007pub const WARNNESTEDVAR: i32 = 181;
4008/// `XTRACE` constant.
4009pub const XTRACE: i32 = 182;
4010/// `USEZLE` constant.
4011pub const USEZLE: i32 = 183;
4012/// `DVORAK` constant.
4013pub const DVORAK: i32 = 184;
4014/// `OPT_SIZE` constant.
4015pub const OPT_SIZE: i32 = 185;
4016/// `OptIndex` type alias.
4017pub type OptIndex = u8; // c:2556
4018
4019// #define isset(X) (opts[X])                                               // c:2559
4020/// Port of `isset` from `Src/zsh.h:2559` — C macro `isset(X)`.
4021/// Returns true if option is set.
4022#[inline]
4023pub fn isset(opt: i32) -> bool {
4024    // c:Src/zsh.h:2557 `#define isset(X) (opts[X])` — a single array load.
4025    // Backed by the extensions::opts_cache fast path (one relaxed atomic
4026    // load) instead of the old name→num→name + RwLock + String-hash path.
4027    crate::opts_cache::is_set(opt)
4028}
4029
4030// #define unset(X) (!opts[X])                                              // c:2560
4031/// Port of `unset` from `Src/zsh.h:2560` — C macro `unset(X)`.
4032/// Returns true if option is NOT set.
4033#[inline]
4034pub fn unset(opt: i32) -> bool {
4035    !isset(opt)
4036}
4037
4038// #define interact (isset(INTERACTIVE))                                    // c:2562
4039/// Port of `interact` from `Src/zsh.h:2562` — C macro.
4040#[inline]
4041pub fn interact() -> bool {
4042    isset(INTERACTIVE)
4043}
4044
4045// #define jobbing  (isset(MONITOR))                                        // c:2563
4046/// Port of `jobbing` from `Src/zsh.h:2563` — C macro.
4047#[inline]
4048pub fn jobbing() -> bool {
4049    isset(MONITOR)
4050}
4051
4052// #define islogin  (isset(LOGINSHELL))                                     // c:2564
4053/// Port of `islogin` from `Src/zsh.h:2564` — C macro.
4054#[inline]
4055pub fn islogin() -> bool {
4056    isset(LOGINSHELL)
4057}
4058
4059// !!! WARNING: RUST-ONLY HELPER !!!
4060//
4061// `opt_name()` has NO C counterpart — do not add a `Port of … from Src/…`
4062// citation to it. C zsh has no option-number → option-name function at all:
4063// the mapping lives in the static `optns[]` table (`Src/options.c:79`) whose
4064// rows carry both the name and the number, and every caller either walks that
4065// table or looks a row up through `optiontab` (`Src/options.c:490-491`,
4066// `optiontab->addnode(optiontab, on->node.nam, on)`). Printing a name goes
4067// through `on->node.nam` directly (`Src/options.c:459-465`).
4068//
4069// The Rust port keeps option numbers as `i32` constants rather than as rows of
4070// a table, so the reverse lookup C gets for free from `optns[]` has to be a
4071// function here. It stands in for the `optns[]` row's `node.nam` field, not
4072// for any C function.
4073/// Helper: convert option constant to its name for lookup.
4074///
4075/// Cached O(1) number→name lookup. The canonical match below has ~185
4076/// arms of `x if x == CONST` guards, which the compiler lowers to a
4077/// LINEAR comparison chain (guards defeat jump-table lowering). It used
4078/// to run per call; `optno_by_name` calls it in a `1..OPT_SIZE` loop,
4079/// so a single `optlookup`/`isset` was O(OPT_SIZE²). That dominated
4080/// startup — compinit issues ~600k isset() calls and effectively hung.
4081/// Building the table once (closure, not a new fn) makes every later
4082/// `opt_name` a single vector index.
4083pub fn opt_name(opt: i32) -> &'static str {
4084    static OPT_NAMES: std::sync::OnceLock<Vec<&'static str>> = std::sync::OnceLock::new();
4085    let table = OPT_NAMES.get_or_init(|| {
4086        let build = |opt: i32| -> &'static str {
4087            match opt {
4088                x if x == ALIASFUNCDEF => "aliasfuncdef",
4089                x if x == ALLEXPORT => "allexport",
4090                x if x == ALWAYSLASTPROMPT => "alwayslastprompt",
4091                x if x == ALWAYSTOEND => "alwaystoend",
4092                x if x == APPENDHISTORY => "appendhistory",
4093                x if x == AUTOCD => "autocd",
4094                x if x == AUTOCONTINUE => "autocontinue",
4095                x if x == AUTOLIST => "autolist",
4096                x if x == AUTOMENU => "automenu",
4097                x if x == AUTONAMEDIRS => "autonamedirs",
4098                x if x == AUTOPARAMKEYS => "autoparamkeys",
4099                x if x == AUTOPARAMSLASH => "autoparamslash",
4100                x if x == AUTOPUSHD => "autopushd",
4101                x if x == AUTOREMOVESLASH => "autoremoveslash",
4102                x if x == AUTORESUME => "autoresume",
4103                x if x == BADPATTERN => "badpattern",
4104                x if x == BANGHIST => "banghist",
4105                x if x == BAREGLOBQUAL => "bareglobqual",
4106                x if x == BASHAUTOLIST => "bashautolist",
4107                x if x == BASHREMATCH => "bashrematch",
4108                x if x == BEEP => "beep",
4109                x if x == BGNICE => "bgnice",
4110                x if x == BRACECCL => "braceccl",
4111                x if x == BSDECHO => "bsdecho",
4112                x if x == CASEGLOB => "caseglob",
4113                x if x == CASEMATCH => "casematch",
4114                x if x == CASEPATHS => "casepaths",
4115                x if x == CBASES => "cbases",
4116                x if x == CDABLEVARS => "cdablevars",
4117                x if x == CDSILENT => "cdsilent",
4118                x if x == CHASEDOTS => "chasedots",
4119                x if x == CHASELINKS => "chaselinks",
4120                x if x == CHECKJOBS => "checkjobs",
4121                x if x == CHECKRUNNINGJOBS => "checkrunningjobs",
4122                x if x == CLOBBER => "clobber",
4123                x if x == CLOBBEREMPTY => "clobberempty",
4124                x if x == APPENDCREATE => "appendcreate",
4125                x if x == COMBININGCHARS => "combiningchars",
4126                x if x == COMPLETEALIASES => "completealiases",
4127                x if x == COMPLETEINWORD => "completeinword",
4128                x if x == CORRECT => "correct",
4129                x if x == CORRECTALL => "correctall",
4130                x if x == CPRECEDENCES => "cprecedences",
4131                x if x == CSHJUNKIEHISTORY => "cshjunkiehistory",
4132                x if x == CSHJUNKIELOOPS => "cshjunkieloops",
4133                x if x == CSHJUNKIEQUOTES => "cshjunkiequotes",
4134                x if x == CSHNULLCMD => "cshnullcmd",
4135                x if x == CSHNULLGLOB => "cshnullglob",
4136                x if x == CONTINUEONERROR => "continueonerror",
4137                x if x == DEBUGBEFORECMD => "debugbeforecmd",
4138                x if x == EMACSMODE => "emacs",
4139                x if x == EQUALSOPT => "equals",
4140                x if x == ERREXIT => "errexit",
4141                x if x == ERRRETURN => "errreturn",
4142                x if x == EXECOPT => "exec",
4143                x if x == EXTENDEDGLOB => "extendedglob",
4144                x if x == EXTENDEDHISTORY => "extendedhistory",
4145                x if x == EVALLINENO => "evallineno",
4146                x if x == FLOWCONTROL => "flowcontrol",
4147                x if x == FORCEFLOAT => "forcefloat",
4148                x if x == FUNCTIONARGZERO => "functionargzero",
4149                x if x == GLOBOPT => "glob",
4150                x if x == GLOBALEXPORT => "globalexport",
4151                x if x == GLOBALRCS => "globalrcs",
4152                x if x == GLOBASSIGN => "globassign",
4153                x if x == GLOBCOMPLETE => "globcomplete",
4154                x if x == GLOBDOTS => "globdots",
4155                x if x == GLOBSTARSHORT => "globstarshort",
4156                x if x == GLOBSUBST => "globsubst",
4157                x if x == HASHCMDS => "hashcmds",
4158                x if x == HASHDIRS => "hashdirs",
4159                x if x == HASHEXECUTABLESONLY => "hashexecutablesonly",
4160                x if x == HASHLISTALL => "hashlistall",
4161                x if x == HISTALLOWCLOBBER => "histallowclobber",
4162                x if x == HISTBEEP => "histbeep",
4163                x if x == HISTEXPIREDUPSFIRST => "histexpiredupsfirst",
4164                x if x == HISTFCNTLLOCK => "histfcntllock",
4165                x if x == HISTFINDNODUPS => "histfindnodups",
4166                x if x == HISTIGNOREALLDUPS => "histignorealldups",
4167                x if x == HISTIGNOREDUPS => "histignoredups",
4168                x if x == HISTIGNORESPACE => "histignorespace",
4169                x if x == HISTLEXWORDS => "histlexwords",
4170                x if x == HISTNOFUNCTIONS => "histnofunctions",
4171                x if x == HISTNOSTORE => "histnostore",
4172                x if x == HISTREDUCEBLANKS => "histreduceblanks",
4173                x if x == HISTSAVEBYCOPY => "histsavebycopy",
4174                x if x == HISTSAVENODUPS => "histsavenodups",
4175                x if x == HISTSUBSTPATTERN => "histsubstpattern",
4176                x if x == HISTVERIFY => "histverify",
4177                x if x == HUP => "hup",
4178                x if x == IGNOREBRACES => "ignorebraces",
4179                x if x == IGNORECLOSEBRACES => "ignoreclosebraces",
4180                x if x == IGNOREEOF => "ignoreeof",
4181                x if x == INCAPPENDHISTORY => "incappendhistory",
4182                x if x == INCAPPENDHISTORYTIME => "incappendhistorytime",
4183                x if x == INTERACTIVE => "interactive",
4184                x if x == INTERACTIVECOMMENTS => "interactivecomments",
4185                x if x == KSHARRAYS => "ksharrays",
4186                x if x == KSHAUTOLOAD => "kshautoload",
4187                x if x == KSHGLOB => "kshglob",
4188                x if x == KSHOPTIONPRINT => "kshoptionprint",
4189                x if x == KSHTYPESET => "kshtypeset",
4190                x if x == KSHZEROSUBSCRIPT => "kshzerosubscript",
4191                x if x == LISTAMBIGUOUS => "listambiguous",
4192                x if x == LISTBEEP => "listbeep",
4193                x if x == LISTPACKED => "listpacked",
4194                x if x == LISTROWSFIRST => "listrowsfirst",
4195                x if x == LISTTYPES => "listtypes",
4196                x if x == LOCALOPTIONS => "localoptions",
4197                x if x == LOCALLOOPS => "localloops",
4198                x if x == LOCALPATTERNS => "localpatterns",
4199                x if x == LOCALTRAPS => "localtraps",
4200                x if x == LOGINSHELL => "loginshell",
4201                x if x == LONGLISTJOBS => "longlistjobs",
4202                x if x == MAGICEQUALSUBST => "magicequalsubst",
4203                x if x == MAILWARNING => "mailwarning",
4204                x if x == MARKDIRS => "markdirs",
4205                x if x == MENUCOMPLETE => "menucomplete",
4206                x if x == MONITOR => "monitor",
4207                x if x == MULTIBYTE => "multibyte",
4208                x if x == MULTIFUNCDEF => "multifuncdef",
4209                x if x == MULTIOS => "multios",
4210                x if x == NOMATCH => "nomatch",
4211                x if x == NOTIFY => "notify",
4212                x if x == NULLGLOB => "nullglob",
4213                x if x == NUMERICGLOBSORT => "numericglobsort",
4214                x if x == OCTALZEROES => "octalzeroes",
4215                x if x == OVERSTRIKE => "overstrike",
4216                x if x == PATHDIRS => "pathdirs",
4217                x if x == PATHSCRIPT => "pathscript",
4218                x if x == PIPEFAIL => "pipefail",
4219                x if x == POSIXALIASES => "posixaliases",
4220                x if x == POSIXARGZERO => "posixargzero",
4221                x if x == POSIXBUILTINS => "posixbuiltins",
4222                x if x == POSIXCD => "posixcd",
4223                x if x == POSIXIDENTIFIERS => "posixidentifiers",
4224                x if x == POSIXJOBS => "posixjobs",
4225                x if x == POSIXSTRINGS => "posixstrings",
4226                x if x == POSIXTRAPS => "posixtraps",
4227                x if x == PRINTEIGHTBIT => "printeightbit",
4228                x if x == PRINTEXITVALUE => "printexitvalue",
4229                x if x == PRIVILEGED => "privileged",
4230                x if x == PROMPTBANG => "promptbang",
4231                x if x == PROMPTCR => "promptcr",
4232                x if x == PROMPTPERCENT => "promptpercent",
4233                x if x == PROMPTSP => "promptsp",
4234                x if x == PROMPTSUBST => "promptsubst",
4235                x if x == PUSHDIGNOREDUPS => "pushdignoredups",
4236                x if x == PUSHDMINUS => "pushdminus",
4237                x if x == PUSHDSILENT => "pushdsilent",
4238                x if x == PUSHDTOHOME => "pushdtohome",
4239                x if x == RCEXPANDPARAM => "rcexpandparam",
4240                x if x == RCQUOTES => "rcquotes",
4241                x if x == RCS => "rcs",
4242                x if x == RECEXACT => "recexact",
4243                x if x == REMATCHPCRE => "rematchpcre",
4244                x if x == RMSTARSILENT => "rmstarsilent",
4245                x if x == RMSTARWAIT => "rmstarwait",
4246                x if x == SHAREHISTORY => "sharehistory",
4247                x if x == SHFILEEXPANSION => "shfileexpansion",
4248                x if x == SHGLOB => "shglob",
4249                x if x == SHINSTDIN => "shinstdin",
4250                x if x == SHNULLCMD => "shnullcmd",
4251                x if x == SHOPTIONLETTERS => "shoptionletters",
4252                x if x == SHORTLOOPS => "shortloops",
4253                x if x == SHORTREPEAT => "shortrepeat",
4254                x if x == SHWORDSPLIT => "shwordsplit",
4255                x if x == SINGLECOMMAND => "singlecommand",
4256                x if x == SINGLELINEZLE => "singlelinezle",
4257                x if x == SOURCETRACE => "sourcetrace",
4258                x if x == SUNKEYBOARDHACK => "sunkeyboardhack",
4259                x if x == TRANSIENTRPROMPT => "transientrprompt",
4260                x if x == TRAPSASYNC => "trapsasync",
4261                x if x == TYPESETSILENT => "typesetsilent",
4262                x if x == TYPESETTOUNSET => "typesettounset",
4263                x if x == UNSET => "unset",
4264                x if x == VERBOSE => "verbose",
4265                x if x == ALIASESOPT => "aliases",
4266                x if x == WARNCREATEGLOBAL => "warncreateglobal",
4267                x if x == WARNNESTEDVAR => "warnnestedvar",
4268                x if x == XTRACE => "xtrace",
4269                x if x == USEZLE => "zle",
4270                x if x == DVORAK => "dvorak",
4271                // VIMODE was missing entirely from the opt_name table.
4272                // The storage key in ZSH_OPTIONS_SET is "vi" (not "vimode")
4273                // — must match so isset(VIMODE) and opt_state_set("vi")
4274                // address the same slot.
4275                x if x == VIMODE => "vi",
4276                _ => "",
4277            }
4278        };
4279        (0..OPT_SIZE).map(build).collect()
4280    });
4281    table.get(opt as usize).copied().unwrap_or("")
4282}
4283
4284// =============================================================================
4285// 36. Terminal control (zsh.h:2633-2680).
4286// =============================================================================
4287/// `TERM_BAD` constant.
4288pub const TERM_BAD: i32 = 0x01;
4289/// `TERM_UNKNOWN` constant.
4290pub const TERM_UNKNOWN: i32 = 0x02;
4291/// `TERM_NOUP` constant.
4292pub const TERM_NOUP: i32 = 0x04;
4293/// `TERM_SHORT` constant.
4294pub const TERM_SHORT: i32 = 0x08;
4295/// `TERM_NARROW` constant.
4296pub const TERM_NARROW: i32 = 0x10;
4297/// `TCCLEARSCREEN` constant.
4298pub const TCCLEARSCREEN: i32 = 0;
4299/// `TCLEFT` constant.
4300pub const TCLEFT: i32 = 1;
4301/// `TCMULTLEFT` constant.
4302pub const TCMULTLEFT: i32 = 2;
4303/// `TCRIGHT` constant.
4304pub const TCRIGHT: i32 = 3;
4305/// `TCMULTRIGHT` constant.
4306pub const TCMULTRIGHT: i32 = 4;
4307/// `TCUP` constant.
4308pub const TCUP: i32 = 5;
4309/// `TCMULTUP` constant.
4310pub const TCMULTUP: i32 = 6;
4311/// `TCDOWN` constant.
4312pub const TCDOWN: i32 = 7;
4313/// `TCMULTDOWN` constant.
4314pub const TCMULTDOWN: i32 = 8;
4315/// `TCDEL` constant.
4316pub const TCDEL: i32 = 9;
4317/// `TCMULTDEL` constant.
4318pub const TCMULTDEL: i32 = 10;
4319/// `TCINS` constant.
4320pub const TCINS: i32 = 11;
4321/// `TCMULTINS` constant.
4322pub const TCMULTINS: i32 = 12;
4323/// `TCCLEAREOD` constant.
4324pub const TCCLEAREOD: i32 = 13;
4325/// `TCCLEAREOL` constant.
4326pub const TCCLEAREOL: i32 = 14;
4327/// `TCINSLINE` constant.
4328pub const TCINSLINE: i32 = 15;
4329/// `TCDELLINE` constant.
4330pub const TCDELLINE: i32 = 16;
4331/// `TCNEXTTAB` constant.
4332pub const TCNEXTTAB: i32 = 17;
4333/// `TCBOLDFACEBEG` constant.
4334pub const TCBOLDFACEBEG: i32 = 18;
4335/// `TCFAINTBEG` constant.
4336pub const TCFAINTBEG: i32 = 19;
4337/// `TCSTANDOUTBEG` constant.
4338pub const TCSTANDOUTBEG: i32 = 20;
4339/// `TCUNDERLINEBEG` constant.
4340pub const TCUNDERLINEBEG: i32 = 21;
4341/// `TCITALICSBEG` constant.
4342pub const TCITALICSBEG: i32 = 22;
4343/// `TCALLATTRSOFF` constant.
4344pub const TCALLATTRSOFF: i32 = 23;
4345/// `TCSTANDOUTEND` constant.
4346pub const TCSTANDOUTEND: i32 = 24;
4347/// `TCUNDERLINEEND` constant.
4348pub const TCUNDERLINEEND: i32 = 25;
4349/// `TCITALICSEND` constant.
4350pub const TCITALICSEND: i32 = 26;
4351/// `TCHORIZPOS` constant.
4352pub const TCHORIZPOS: i32 = 27;
4353/// `TCUPCURSOR` constant.
4354pub const TCUPCURSOR: i32 = 28;
4355/// `TCDOWNCURSOR` constant.
4356pub const TCDOWNCURSOR: i32 = 29;
4357/// `TCLEFTCURSOR` constant.
4358pub const TCLEFTCURSOR: i32 = 30;
4359/// `TCRIGHTCURSOR` constant.
4360pub const TCRIGHTCURSOR: i32 = 31;
4361/// `TCSAVECURSOR` constant.
4362pub const TCSAVECURSOR: i32 = 32;
4363/// `TCRESTRCURSOR` constant.
4364pub const TCRESTRCURSOR: i32 = 33;
4365/// `TCBACKSPACE` constant.
4366pub const TCBACKSPACE: i32 = 34;
4367/// `TCFGCOLOUR` constant.
4368pub const TCFGCOLOUR: i32 = 35;
4369/// `TCBGCOLOUR` constant.
4370pub const TCBGCOLOUR: i32 = 36;
4371/// `TCCURINV` constant.
4372pub const TCCURINV: i32 = 37;
4373/// `TCCURVIS` constant.
4374pub const TCCURVIS: i32 = 38;
4375/// `TC_COUNT` constant.
4376pub const TC_COUNT: i32 = 39;
4377
4378// =============================================================================
4379// 37. Text attributes (zattr) (zsh.h:2689-2750).
4380// =============================================================================
4381/// `zattr` type alias.
4382pub type zattr = u64; // c:2689
4383/// `TXTBOLDFACE` constant.
4384pub const TXTBOLDFACE: zattr = 0x0001;
4385/// `TXTFAINT` constant.
4386pub const TXTFAINT: zattr = 0x0002;
4387/// `TXTSTANDOUT` constant.
4388pub const TXTSTANDOUT: zattr = 0x0004;
4389/// `TXTUNDERLINE` constant.
4390pub const TXTUNDERLINE: zattr = 0x0008;
4391/// `TXTITALIC` constant.
4392pub const TXTITALIC: zattr = 0x0010;
4393/// `TXTFGCOLOUR` constant.
4394pub const TXTFGCOLOUR: zattr = 0x0020;
4395/// `TXTBGCOLOUR` constant.
4396pub const TXTBGCOLOUR: zattr = 0x0040;
4397/// `TXT_ATTR_ALL` constant.
4398pub const TXT_ATTR_ALL: zattr = 0x007F;
4399/// `TXT_MULTIWORD_MASK` constant.
4400pub const TXT_MULTIWORD_MASK: zattr = 0x0400;
4401/// `TXT_ERROR` constant.
4402pub const TXT_ERROR: zattr = 0xF00000F000000003;
4403/// `TXT_ATTR_FONT_WEIGHT` constant.
4404pub const TXT_ATTR_FONT_WEIGHT: zattr = TXTBOLDFACE | TXTFAINT;
4405/// `TXT_ATTR_FG_COL_MASK` constant.
4406pub const TXT_ATTR_FG_COL_MASK: zattr = 0x000000FFFFFF0000;
4407/// `TXT_ATTR_FG_COL_SHIFT` constant.
4408pub const TXT_ATTR_FG_COL_SHIFT: u32 = 16;
4409/// `TXT_ATTR_BG_COL_MASK` constant.
4410pub const TXT_ATTR_BG_COL_MASK: zattr = 0xFFFFFF0000000000;
4411/// `TXT_ATTR_BG_COL_SHIFT` constant.
4412pub const TXT_ATTR_BG_COL_SHIFT: u32 = 40;
4413/// `TXT_ATTR_FG_24BIT` constant.
4414pub const TXT_ATTR_FG_24BIT: zattr = 0x4000;
4415/// `TXT_ATTR_BG_24BIT` constant.
4416pub const TXT_ATTR_BG_24BIT: zattr = 0x8000;
4417/// `TXT_ATTR_FG_MASK` constant.
4418pub const TXT_ATTR_FG_MASK: zattr = TXTFGCOLOUR | TXT_ATTR_FG_COL_MASK | TXT_ATTR_FG_24BIT;
4419/// `TXT_ATTR_BG_MASK` constant.
4420pub const TXT_ATTR_BG_MASK: zattr = TXTBGCOLOUR | TXT_ATTR_BG_COL_MASK | TXT_ATTR_BG_24BIT;
4421/// `TXT_ATTR_COLOUR_MASK` constant.
4422pub const TXT_ATTR_COLOUR_MASK: zattr = TXT_ATTR_FG_MASK | TXT_ATTR_BG_MASK;
4423
4424// zsh 5.9.x "off" attribute bits (5.9.1 zsh.h:2683-2701). The release
4425// zshrs targets (5.9.2) marks the LAST cell of a highlighted region with
4426// these so `zwcputc` turns the region's attributes off straight after
4427// the character (5.9.1 zle_refresh.c:1291-1293, :669-675). The dev tree
4428// dropped them along with the layout they lived in; in 5.9.x they sit
4429// at 0x0020-0x0200, which this (dev) layout uses for TXTFGCOLOUR /
4430// TXTBGCOLOUR, so they occupy bits this layout leaves free. Only
4431// zle_refresh's region painter and `zwcputc` set or read them.
4432/// `TXTNOBOLDFACE` (5.9.1 zsh.h:2689).
4433pub const TXTNOBOLDFACE: zattr = 0x0080;
4434/// `TXTNOSTANDOUT` (5.9.1 zsh.h:2690).
4435pub const TXTNOSTANDOUT: zattr = 0x0100;
4436/// `TXTNOUNDERLINE` (5.9.1 zsh.h:2691).
4437pub const TXTNOUNDERLINE: zattr = 0x0200;
4438/// `TXTNOFGCOLOUR` (5.9.1 zsh.h:2692).
4439pub const TXTNOFGCOLOUR: zattr = 0x0800;
4440/// `TXTNOBGCOLOUR` (5.9.1 zsh.h:2693).
4441pub const TXTNOBGCOLOUR: zattr = 0x1000;
4442/// `TXT_ATTR_OFF_MASK` (5.9.1 zsh.h:2695).
4443pub const TXT_ATTR_OFF_MASK: zattr =
4444    TXTNOBOLDFACE | TXTNOSTANDOUT | TXTNOUNDERLINE | TXTNOFGCOLOUR | TXTNOBGCOLOUR;
4445/// `TXT_ATTR_ON_VALUES_MASK` (5.9.1 zsh.h:2726-2728): every "on" flag
4446/// plus the colour values and their 24-bit markers.
4447pub const TXT_ATTR_ON_VALUES_MASK: zattr = TXT_ATTR_ALL
4448    | TXT_ATTR_FG_COL_MASK
4449    | TXT_ATTR_BG_COL_MASK
4450    | TXT_ATTR_FG_24BIT
4451    | TXT_ATTR_BG_24BIT;
4452/// `TXT_ATTR_COLOUR_ON_MASK` (5.9.1 zsh.h:2739-2740).
4453pub const TXT_ATTR_COLOUR_ON_MASK: zattr = TXT_ATTR_FG_MASK | TXT_ATTR_BG_MASK;
4454/// The `(on, off)` bit pairs behind 5.9.x `TXT_ATTR_OFF_FROM_ON` /
4455/// `TXT_ATTR_ON_FROM_OFF` (5.9.1 zsh.h:2697-2701). 5.9.x converts with a
4456/// shift of `TXT_ATTR_OFF_ON_SHIFT`; the off bits are not contiguous in
4457/// this layout, so the pairs are spelled out.
4458pub const TXT_ATTR_OFF_ON_PAIRS: [(zattr, zattr); 5] = [
4459    (TXTBOLDFACE, TXTNOBOLDFACE),
4460    (TXTSTANDOUT, TXTNOSTANDOUT),
4461    (TXTUNDERLINE, TXTNOUNDERLINE),
4462    (TXTFGCOLOUR, TXTNOFGCOLOUR),
4463    (TXTBGCOLOUR, TXTNOBGCOLOUR),
4464];
4465/// `COL_SEQ_FG` constant.
4466pub const COL_SEQ_FG: i32 = 0;
4467/// `COL_SEQ_BG` constant.
4468pub const COL_SEQ_BG: i32 = 1;
4469/// `color_rgb` — see fields for layout.
4470#[allow(non_camel_case_types)]
4471pub struct color_rgb {
4472    // c:2752
4473    /// `red` field.
4474    pub red: u32,
4475    /// `green` field.
4476    pub green: u32,
4477    /// `blue` field.
4478    pub blue: u32,
4479}
4480/// `Color_rgb` type alias.
4481pub type Color_rgb = Box<color_rgb>;
4482/// `TSC_RAW` constant.
4483pub const TSC_RAW: i32 = 0x0001; // c:2764
4484/// `TSC_PROMPT` constant.
4485pub const TSC_PROMPT: i32 = 0x0002;
4486
4487// =============================================================================
4488// 38. Prompt %_ command stack (zsh.h:2773-2809).
4489// =============================================================================
4490/// `CMDSTACKSZ` constant.
4491pub const CMDSTACKSZ: usize = 256;
4492/// `CS_FOR` constant.
4493pub const CS_FOR: i32 = 0;
4494/// `CS_WHILE` constant.
4495pub const CS_WHILE: i32 = 1;
4496/// `CS_REPEAT` constant.
4497pub const CS_REPEAT: i32 = 2;
4498/// `CS_SELECT` constant.
4499pub const CS_SELECT: i32 = 3;
4500/// `CS_UNTIL` constant.
4501pub const CS_UNTIL: i32 = 4;
4502/// `CS_IF` constant.
4503pub const CS_IF: i32 = 5;
4504/// `CS_IFTHEN` constant.
4505pub const CS_IFTHEN: i32 = 6;
4506/// `CS_ELSE` constant.
4507pub const CS_ELSE: i32 = 7;
4508/// `CS_ELIF` constant.
4509pub const CS_ELIF: i32 = 8;
4510/// `CS_MATH` constant.
4511pub const CS_MATH: i32 = 9;
4512/// `CS_COND` constant.
4513pub const CS_COND: i32 = 10;
4514/// `CS_CMDOR` constant.
4515pub const CS_CMDOR: i32 = 11;
4516/// `CS_CMDAND` constant.
4517pub const CS_CMDAND: i32 = 12;
4518/// `CS_PIPE` constant.
4519pub const CS_PIPE: i32 = 13;
4520/// `CS_ERRPIPE` constant.
4521pub const CS_ERRPIPE: i32 = 14;
4522/// `CS_FOREACH` constant.
4523pub const CS_FOREACH: i32 = 15;
4524/// `CS_CASE` constant.
4525pub const CS_CASE: i32 = 16;
4526/// `CS_FUNCDEF` constant.
4527pub const CS_FUNCDEF: i32 = 17;
4528/// `CS_SUBSH` constant.
4529pub const CS_SUBSH: i32 = 18;
4530/// `CS_CURSH` constant.
4531pub const CS_CURSH: i32 = 19;
4532/// `CS_ARRAY` constant.
4533pub const CS_ARRAY: i32 = 20;
4534/// `CS_QUOTE` constant.
4535pub const CS_QUOTE: i32 = 21;
4536/// `CS_DQUOTE` constant.
4537pub const CS_DQUOTE: i32 = 22;
4538/// `CS_BQUOTE` constant.
4539pub const CS_BQUOTE: i32 = 23;
4540/// `CS_CMDSUBST` constant.
4541pub const CS_CMDSUBST: i32 = 24;
4542/// `CS_MATHSUBST` constant.
4543pub const CS_MATHSUBST: i32 = 25;
4544/// `CS_ELIFTHEN` constant.
4545pub const CS_ELIFTHEN: i32 = 26;
4546/// `CS_HEREDOC` constant.
4547pub const CS_HEREDOC: i32 = 27;
4548/// `CS_HEREDOCD` constant.
4549pub const CS_HEREDOCD: i32 = 28;
4550/// `CS_BRACE` constant.
4551pub const CS_BRACE: i32 = 29;
4552/// `CS_BRACEPAR` constant.
4553pub const CS_BRACEPAR: i32 = 30;
4554/// `CS_ALWAYS` constant.
4555pub const CS_ALWAYS: i32 = 31;
4556/// `CS_COUNT` constant.
4557pub const CS_COUNT: i32 = 32;
4558
4559// =============================================================================
4560// 39. Heap memory + Heapid (zsh.h:2826-2862).
4561// =============================================================================
4562/// `Heapid` type alias.
4563pub type Heapid = u32; // c:2826
4564/// `HEAPID_PERMANENT` constant.
4565pub const HEAPID_PERMANENT: Heapid = u32::MAX; // c:2834
4566/// `HDV_PUSH` constant.
4567pub const HDV_PUSH: i32 = 0x01;
4568/// `HDV_POP` constant.
4569pub const HDV_POP: i32 = 0x02;
4570/// `HDV_CREATE` constant.
4571pub const HDV_CREATE: i32 = 0x04;
4572/// `HDV_FREE` constant.
4573pub const HDV_FREE: i32 = 0x08;
4574/// `HDV_NEW` constant.
4575pub const HDV_NEW: i32 = 0x10;
4576/// `HDV_OLD` constant.
4577pub const HDV_OLD: i32 = 0x20;
4578/// `HDV_SWITCH` constant.
4579pub const HDV_SWITCH: i32 = 0x40;
4580/// `HDV_ALLOC` constant.
4581pub const HDV_ALLOC: i32 = 0x80;
4582
4583// =============================================================================
4584// 40. Signal trap state (zsh.h:2935-2984).
4585// =============================================================================
4586/// `ZSIG_TRAPPED` constant.
4587pub const ZSIG_TRAPPED: i32 = 1 << 0;
4588/// `ZSIG_IGNORED` constant.
4589pub const ZSIG_IGNORED: i32 = 1 << 1;
4590/// `ZSIG_FUNC` constant.
4591pub const ZSIG_FUNC: i32 = 1 << 2;
4592/// `ZSIG_MASK` constant.
4593pub const ZSIG_MASK: i32 = ZSIG_TRAPPED | ZSIG_IGNORED | ZSIG_FUNC;
4594/// `ZSIG_ALIAS` constant.
4595pub const ZSIG_ALIAS: i32 = 1 << 3;
4596/// `ZSIG_SHIFT` constant.
4597pub const ZSIG_SHIFT: i32 = 4;
4598/// `TRAP_STATE_INACTIVE` constant.
4599pub const TRAP_STATE_INACTIVE: i32 = 0;
4600/// `TRAP_STATE_PRIMED` constant.
4601pub const TRAP_STATE_PRIMED: i32 = 1;
4602/// `TRAP_STATE_FORCE_RETURN` constant.
4603pub const TRAP_STATE_FORCE_RETURN: i32 = 2;
4604/// `ERRFLAG_ERROR` constant.
4605pub const ERRFLAG_ERROR: i32 = 1;
4606/// `ERRFLAG_INT` constant.
4607pub const ERRFLAG_INT: i32 = 2;
4608/// `ERRFLAG_HARD` constant.
4609pub const ERRFLAG_HARD: i32 = 4;
4610
4611// =============================================================================
4612// 41. Sorting (zsh.h:2992-3008).
4613// =============================================================================
4614/// `SORTIT_ANYOLDHOW` constant.
4615pub const SORTIT_ANYOLDHOW: i32 = 0;
4616/// `SORTIT_IGNORING_CASE` constant.
4617pub const SORTIT_IGNORING_CASE: i32 = 1;
4618/// `SORTIT_NUMERICALLY` constant.
4619pub const SORTIT_NUMERICALLY: i32 = 2;
4620/// `SORTIT_NUMERICALLY_SIGNED` constant.
4621pub const SORTIT_NUMERICALLY_SIGNED: i32 = 4;
4622/// `SORTIT_BACKWARDS` constant.
4623pub const SORTIT_BACKWARDS: i32 = 8;
4624/// `SORTIT_IGNORING_BACKSLASHES` constant.
4625pub const SORTIT_IGNORING_BACKSLASHES: i32 = 16;
4626/// `SORTIT_SOMEHOW` constant.
4627pub const SORTIT_SOMEHOW: i32 = 32;
4628
4629// =============================================================================
4630// 42. Case modify + Getkey (zsh.h:3122-3197).
4631// =============================================================================
4632/// `CASMOD_NONE` constant.
4633pub const CASMOD_NONE: i32 = 0;
4634/// `CASMOD_UPPER` constant.
4635pub const CASMOD_UPPER: i32 = 1;
4636/// `CASMOD_LOWER` constant.
4637pub const CASMOD_LOWER: i32 = 2;
4638/// `CASMOD_CAPS` constant.
4639pub const CASMOD_CAPS: i32 = 3;
4640/// `GETKEY_OCTAL_ESC` constant.
4641pub const GETKEY_OCTAL_ESC: i32 = 1 << 0;
4642/// `GETKEY_EMACS` constant.
4643pub const GETKEY_EMACS: i32 = 1 << 1;
4644/// `GETKEY_CTRL` constant.
4645pub const GETKEY_CTRL: i32 = 1 << 2;
4646/// `GETKEY_BACKSLASH_C` constant.
4647pub const GETKEY_BACKSLASH_C: i32 = 1 << 3;
4648/// `GETKEY_DOLLAR_QUOTE` constant.
4649pub const GETKEY_DOLLAR_QUOTE: i32 = 1 << 4;
4650/// `GETKEY_BACKSLASH_MINUS` constant.
4651pub const GETKEY_BACKSLASH_MINUS: i32 = 1 << 5;
4652/// `GETKEY_SINGLE_CHAR` constant.
4653pub const GETKEY_SINGLE_CHAR: i32 = 1 << 6;
4654/// `GETKEY_UPDATE_OFFSET` constant.
4655pub const GETKEY_UPDATE_OFFSET: i32 = 1 << 7;
4656/// `GETKEY_PRINTF_PERCENT` constant.
4657pub const GETKEY_PRINTF_PERCENT: i32 = 1 << 8;
4658/// `GETKEYS_ECHO` constant.
4659pub const GETKEYS_ECHO: i32 = GETKEY_BACKSLASH_C;
4660/// `GETKEYS_PRINTF_FMT` constant.
4661pub const GETKEYS_PRINTF_FMT: i32 = GETKEY_OCTAL_ESC | GETKEY_BACKSLASH_C | GETKEY_PRINTF_PERCENT;
4662/// `GETKEYS_PRINTF_ARG` constant.
4663pub const GETKEYS_PRINTF_ARG: i32 = GETKEY_BACKSLASH_C;
4664/// `GETKEYS_PRINT` constant.
4665pub const GETKEYS_PRINT: i32 = GETKEY_OCTAL_ESC | GETKEY_BACKSLASH_C | GETKEY_EMACS;
4666/// `GETKEYS_BINDKEY` constant.
4667pub const GETKEYS_BINDKEY: i32 = GETKEY_OCTAL_ESC | GETKEY_EMACS | GETKEY_CTRL;
4668/// `GETKEYS_DOLLARS_QUOTE` constant.
4669pub const GETKEYS_DOLLARS_QUOTE: i32 = GETKEY_OCTAL_ESC | GETKEY_EMACS | GETKEY_DOLLAR_QUOTE;
4670/// `GETKEYS_MATH` constant.
4671pub const GETKEYS_MATH: i32 = GETKEY_OCTAL_ESC | GETKEY_EMACS | GETKEY_CTRL | GETKEY_SINGLE_CHAR;
4672/// `GETKEYS_SEP` constant.
4673pub const GETKEYS_SEP: i32 = GETKEY_OCTAL_ESC | GETKEY_EMACS;
4674pub const GETKEYS_SUFFIX: i32 =
4675    GETKEY_OCTAL_ESC | GETKEY_EMACS | GETKEY_CTRL | GETKEY_BACKSLASH_MINUS;
4676
4677// =============================================================================
4678// 43. zle flags (zsh.h:3203-3216).
4679// =============================================================================
4680/// `ZLRF_HISTORY` constant.
4681pub const ZLRF_HISTORY: i32 = 0x01;
4682/// `ZLRF_NOSETTY` constant.
4683pub const ZLRF_NOSETTY: i32 = 0x02;
4684/// `ZLRF_IGNOREEOF` constant.
4685pub const ZLRF_IGNOREEOF: i32 = 0x04;
4686/// `ZLCON_LINE_START` constant.
4687pub const ZLCON_LINE_START: i32 = 0;
4688/// `ZLCON_LINE_CONT` constant.
4689pub const ZLCON_LINE_CONT: i32 = 1;
4690/// `ZLCON_SELECT` constant.
4691pub const ZLCON_SELECT: i32 = 2;
4692/// `ZLCON_VARED` constant.
4693pub const ZLCON_VARED: i32 = 3;
4694/// `ZLE_CMD_GET_LINE` constant.
4695pub const ZLE_CMD_GET_LINE: i32 = 0;
4696/// `ZLE_CMD_READ` constant.
4697pub const ZLE_CMD_READ: i32 = 1;
4698/// `ZLE_CMD_ADD_TO_LINE` constant.
4699pub const ZLE_CMD_ADD_TO_LINE: i32 = 2;
4700/// `ZLE_CMD_TRASH` constant.
4701pub const ZLE_CMD_TRASH: i32 = 3;
4702/// `ZLE_CMD_RESET_PROMPT` constant.
4703pub const ZLE_CMD_RESET_PROMPT: i32 = 4;
4704/// `ZLE_CMD_REFRESH` constant.
4705pub const ZLE_CMD_REFRESH: i32 = 5;
4706/// `ZLE_CMD_SET_KEYMAP` constant.
4707pub const ZLE_CMD_SET_KEYMAP: i32 = 6;
4708/// `ZLE_CMD_GET_KEY` constant.
4709pub const ZLE_CMD_GET_KEY: i32 = 7;
4710/// `ZLE_CMD_SET_HIST_LINE` constant.
4711pub const ZLE_CMD_SET_HIST_LINE: i32 = 8;
4712/// `ZLE_CMD_PREEXEC` constant.
4713pub const ZLE_CMD_PREEXEC: i32 = 9;
4714/// `ZLE_CMD_POSTEXEC` constant.
4715pub const ZLE_CMD_POSTEXEC: i32 = 10;
4716/// `ZLE_CMD_CHPWD` constant.
4717pub const ZLE_CMD_CHPWD: i32 = 11;
4718
4719// =============================================================================
4720// 44. zexit + nice format (zsh.h:3252-3268).
4721// =============================================================================
4722/// `ZEXIT_NORMAL` constant.
4723pub const ZEXIT_NORMAL: i32 = 0;
4724/// `ZEXIT_SIGNAL` constant.
4725pub const ZEXIT_SIGNAL: i32 = 1;
4726/// `ZEXIT_DEFERRED` constant.
4727pub const ZEXIT_DEFERRED: i32 = 2;
4728/// `NICEFLAG_HEAP` constant.
4729pub const NICEFLAG_HEAP: i32 = 1;
4730/// `NICEFLAG_QUOTE` constant.
4731pub const NICEFLAG_QUOTE: i32 = 2;
4732/// `NICEFLAG_NODUP` constant.
4733pub const NICEFLAG_NODUP: i32 = 4;
4734
4735// =============================================================================
4736// 45. Multibyte macros (zsh.h:3271-3375).
4737// =============================================================================
4738/// `convchar_t` type alias.
4739pub type convchar_t = u32; // c:3276/3357
4740/// `MB_INCOMPLETE` constant.
4741pub const MB_INCOMPLETE: usize = usize::MAX - 1; // c:3313
4742/// `MB_INVALID` constant.
4743pub const MB_INVALID: usize = usize::MAX; // c:3314
4744/// `MB_CUR_MAX` constant.
4745pub const MB_CUR_MAX: usize = 6; // c:3324
4746
4747/// Port of `MB_METACHARINIT` from `Src/zsh.h:3275/3356` — C macro `MB_METACHARINIT()`. C calls
4748/// `mb_charinit()` to reset multibyte state. Rust char iteration is
4749/// stateless; no-op.
4750#[inline]
4751#[allow(non_snake_case)]
4752pub fn MB_METACHARINIT() {} // c:3275
4753
4754/// Port of `MB_METACHARLEN` from `Src/zsh.h:3278/3359` — C macro `MB_METACHARLEN(str)`. Returns
4755/// the byte length of the next metafied character. C: `*str == Meta
4756/// ? 2 : 1` (non-multibyte); `mb_metacharlenconv(str, NULL)`
4757/// (multibyte). Rust returns the same byte length.
4758#[inline]
4759#[allow(non_snake_case)]
4760pub fn MB_METACHARLEN(s: &[u8]) -> usize {
4761    // c:3278/3359
4762    if s.is_empty() {
4763        0
4764    } else if s[0] == Meta {
4765        2
4766    } else {
4767        1
4768    }
4769}
4770
4771/// Port of `MB_METACHARLENCONV` from `Src/zsh.h:3277/3358` — C macro `MB_METACHARLENCONV(str, cp)`.
4772/// Returns byte length + (optionally) the converted char. Rust port
4773/// returns `(byte_len, Option<char>)`.
4774#[inline]
4775#[allow(non_snake_case)]
4776pub fn MB_METACHARLENCONV(s: &[u8]) -> (usize, Option<char>) {
4777    // c:3277
4778    if s.is_empty() {
4779        return (0, None);
4780    }
4781    if s[0] == Meta && s.len() >= 2 {
4782        let unmeta = s[1] ^ 0x20;
4783        (2, Some(unmeta as char))
4784    } else {
4785        (1, Some(s[0] as char))
4786    }
4787}
4788
4789/// Port of `MB_METASTRLEN` from `Src/zsh.h:3279` — C macro `MB_METASTRLEN(str)`. Counts
4790/// multibyte characters in a metafied string.
4791///
4792/// C is a one-line delegation — `mb_metastrlenend(str, 0, NULL)` — and
4793/// this now delegates too. It previously inlined a `Meta`-pair byte
4794/// walk, which is the NON-multibyte build's macro (c:3360 `ztrlen`),
4795/// not this one: it counted a real multibyte character once per BYTE
4796/// and never consulted the MULTIBYTE option. Nothing in the tree calls
4797/// this function directly, but `MB_METASTRWIDTH` below delegated to it,
4798/// which is how a display-WIDTH request became a character count.
4799#[inline]
4800#[allow(non_snake_case)]
4801pub fn MB_METASTRLEN(s: &str) -> usize {
4802    // c:3279 — mb_metastrlenend(str, 0, NULL); NULL eptr ⇒ whole string.
4803    crate::ported::utils::mb_metastrlenend(s, 0, s.len())
4804}
4805
4806/// Port of `MB_METASTRWIDTH` from `Src/zsh.h:3280` — C macro `MB_METASTRWIDTH(str)`. Total display
4807/// WIDTH (columns) of a metafied string — a wide CJK character counts
4808/// 2, a combining character 0.
4809///
4810/// C: `mb_metastrlenend(str, 1, NULL)`. This used to alias
4811/// `MB_METASTRLEN`, returning a character count, so every caller that
4812/// asked for columns got characters: the `select` menu laid
4813/// `日本語` out as 3 columns instead of 6 and packed rows zsh keeps
4814/// separate (c:Src/loop.c:385), and prompt truncation measured the
4815/// same way (c:Src/prompt.c:1359).
4816#[inline]
4817#[allow(non_snake_case)]
4818pub fn MB_METASTRWIDTH(s: &str) -> usize {
4819    // c:3280 — mb_metastrlenend(str, 1, NULL).
4820    crate::ported::utils::mb_metastrlenend(s, 1, s.len())
4821}
4822
4823/// Port of `MB_METASTRLEN2` from `Src/zsh.h:3281/3362` — C macro `MB_METASTRLEN2(str, widthp)`.
4824/// Variant that returns either char count or width depending on
4825/// `widthp`.
4826#[inline]
4827#[allow(non_snake_case)]
4828pub fn MB_METASTRLEN2(s: &str, widthp: i32) -> usize {
4829    // c:3281 — `mb_metastrlenend(str, widthp, NULL)`: `widthp` is C's
4830    // `int`, passed through verbatim. It is the `(m)` flag's
4831    // `multi_width` counter (c:2376), so 0 counts characters, 1 counts
4832    // display columns and 2+ counts one per printable character; a
4833    // `bool` here collapsed 1 and 2+.
4834    crate::ported::utils::mb_metastrlenend(s, widthp, s.len())
4835}
4836
4837/// Port of `MB_CHARINIT` from `Src/zsh.h:3286/3365` — C macro `MB_CHARINIT()`. No-op
4838/// counterpart of `MB_METACHARINIT` for unmetafied input.
4839#[inline]
4840#[allow(non_snake_case)]
4841pub fn MB_CHARINIT() {} // c:3286
4842
4843/// Port of `MB_CHARLEN` from `Src/zsh.h:3288/3367` — C macro `MB_CHARLEN(str, len)`. Byte
4844/// length of the next char in an unmetafied byte string.
4845#[inline]
4846#[allow(non_snake_case)]
4847pub fn MB_CHARLEN(s: &[u8], len: usize) -> usize {
4848    // c:3288
4849    if len == 0 || s.is_empty() {
4850        0
4851    } else {
4852        1
4853    }
4854}
4855
4856/// Port of `MB_CHARLENCONV` from `Src/zsh.h:3287/3366` — C macro `MB_CHARLENCONV(str, len, cp)`.
4857/// Byte length + converted char of the next char in an unmetafied
4858/// byte string.
4859#[inline]
4860#[allow(non_snake_case)]
4861pub fn MB_CHARLENCONV(s: &[u8], len: usize) -> (usize, Option<char>) {
4862    // c:3287
4863    if len == 0 || s.is_empty() {
4864        (0, None)
4865    } else {
4866        (1, Some(s[0] as char))
4867    }
4868}
4869
4870/// Port of `WCWIDTH` from `Src/zsh.h:3300` — C macro `WCWIDTH(wc)`. Display width of a
4871/// wide character: 0 for combining marks / control chars, 2 for
4872/// CJK-wide / emoji, 1 otherwise.
4873///
4874/// Delegates to the `unicode-width` crate (the same data path
4875/// `crate::ported::compat::u9_wcwidth` uses) so combining-mark
4876/// detection comes from the latest UCD. The previous inline
4877/// CJK-only rule returned 1 for combining marks (e.g. U+0301
4878/// combining-acute), which broke `IS_COMBINING(wc)` =
4879/// `WCWIDTH(wc) == 0` and silently disabled every cluster-walk
4880/// codepath that depended on it (alignmultiwordleft / right,
4881/// inccs / deccs realign).
4882#[inline]
4883#[allow(non_snake_case)]
4884pub fn WCWIDTH(wc: char) -> i32 {
4885    // c:3300 — `#define WCWIDTH(wc) wcwidth(wc)`: control chars are -1
4886    // per wcwidth(3), NOT 0. The previous 0 fallback made
4887    // IS_COMBINING('\n') true (c:3343 gates on `WCWIDTH(wc) == 0`), so
4888    // the refresh build's combining-cluster absorption swallowed hard
4889    // newlines into the preceding char's cluster — a multiline BUFFER
4890    // flattened to ONE video row, nextline never ran, and every later
4891    // frame painted anchored rows below where VLN said (zsh-expand's
4892    // multiline `i ` snippet scrambled the display on each keystroke).
4893    unicode_width::UnicodeWidthChar::width(wc)
4894        .map(|w| w as i32)
4895        .unwrap_or_else(|| if wc.is_control() { -1 } else { 1 })
4896}
4897
4898/// Port of `WCWIDTH_WINT` from `Src/zsh.h:3311/3369` — C macro `WCWIDTH_WINT(wc)`.
4899/// `Src/zsh.h:3311` expands it to `zwcwidth(wc)` (`Src/utils.c:729-741`), and
4900/// `Src/zsh.h:3371` defines it as the constant `1` in a non-multibyte build:
4901///
4902/// ```text
4903///     int
4904///     zwcwidth(wint_t wc)
4905///     {
4906///         int wcw;
4907///         /* assume a single-byte character if not valid */
4908///         if (wc == WEOF || unset(MULTIBYTE))
4909///             return 1;
4910///         wcw = WCWIDTH(wc);
4911///         /* if not printable, assume width 1 */
4912///         if (wcw < 0)
4913///             return 1;
4914///         return wcw;
4915///     }
4916/// ```
4917///
4918/// The two clamps are the whole point of the wrapper and were missing: bare
4919/// `WCWIDTH` answers -1 for a control character (per `wcwidth(3)`), so a
4920/// caller measuring a string that carries ANSI escapes or a `\n` would have
4921/// SUBTRACTED columns for them. Combining marks (width 0) and wide glyphs
4922/// (width 2) still report their real width — only the negative case is
4923/// clamped. `WEOF` has no `char` spelling in Rust (an undecodable byte never
4924/// reaches here as a `char`), so only the `unset(MULTIBYTE)` half of c:734
4925/// applies.
4926///
4927/// `zwcwidth` itself is already ported at `Src/utils.c:729` →
4928/// `crate::ported::utils::zwcwidth`; this is the macro that expands to it, so
4929/// it delegates rather than carrying a second copy of the two clamps.
4930#[inline]
4931#[allow(non_snake_case)]
4932pub fn WCWIDTH_WINT(wc: char) -> i32 {
4933    // c:3311 — `#define WCWIDTH_WINT(wc) zwcwidth(wc)`
4934    crate::ported::utils::zwcwidth(wc) as i32
4935}
4936
4937/// Port of `IS_COMBINING` from `Src/zsh.h:3343` — C macro `IS_COMBINING(wc)`. True iff `wc`
4938/// is a non-zero combining character (zero display width).
4939#[inline]
4940#[allow(non_snake_case)]
4941pub fn IS_COMBINING(wc: char) -> bool {
4942    // c:3343
4943    wc as u32 != 0 && WCWIDTH(wc) == 0
4944}
4945
4946/// Port of `IS_BASECHAR` from `Src/zsh.h:3352` — C macro `IS_BASECHAR(wc)`. True iff `wc`
4947/// is a graphic character with non-zero width (suitable as base for
4948/// a combining character).
4949#[inline]
4950#[allow(non_snake_case)]
4951pub fn IS_BASECHAR(wc: char) -> bool {
4952    // c:3352
4953    !wc.is_whitespace() && !wc.is_control() && WCWIDTH(wc) > 0
4954}
4955
4956/// Port of `ZWC` from `Src/zsh.h:3328/3372` — C macro `ZWC(c)`. C casts a char
4957/// literal to `wchar_t` via the `L` prefix (`L'a'`). Rust's `char`
4958/// is already 32-bit Unicode; the cast is a no-op.
4959#[inline]
4960#[allow(non_snake_case)]
4961pub const fn ZWC(c: char) -> char {
4962    c
4963} // c:3328
4964
4965// =============================================================================
4966// 46. Options accessor compat (already-allowed alias for OPT_*).
4967// =============================================================================
4968
4969/// Port of `OPT_ARG` from `Src/zsh.h:1412` — C macro `OPT_ARG(ops, c)` —
4970/// `((ops)->args[((ops)->ind[c] >> 2) - 1])`. Returns the argument
4971/// associated with option `c`. Caller must have already checked
4972/// `OPT_HASARG(ops,c)`; out-of-range indices yield `None` (C would
4973/// dereference past `args[]`, which is undefined — Rust port stays
4974/// safe).
4975#[inline]
4976#[allow(non_snake_case)]
4977pub fn OPT_ARG<'a>(ops: &'a options, c: u8) -> Option<&'a str> {
4978    let idx = (ops.ind[c as usize] >> 2) as usize;
4979    if idx == 0 {
4980        return None;
4981    }
4982    ops.args.get(idx - 1).map(|s| s.as_str())
4983}
4984
4985/// Port of `OPT_ARG_SAFE` from `Src/zsh.h:1414` — C macro `OPT_ARG_SAFE(ops, c)` —
4986/// `(OPT_HASARG(ops,c) ? OPT_ARG(ops,c) : NULL)`.
4987#[inline]
4988#[allow(non_snake_case)]
4989pub fn OPT_ARG_SAFE<'a>(ops: &'a options, c: u8) -> Option<&'a str> {
4990    if OPT_HASARG(ops, c) {
4991        OPT_ARG(ops, c)
4992    } else {
4993        None
4994    }
4995}
4996
4997// Suppress dead-code warnings for the AtomicI32 we don't use yet.
4998#[allow(dead_code)]
4999const _MARKER_KEEP: AtomicI32 = AtomicI32::new(0);
5000
5001#[cfg(test)]
5002mod tests {
5003    use super::*;
5004
5005    #[test]
5006    fn zlong_zulong_sizes() {
5007        let _g = crate::test_util::global_state_lock();
5008        assert_eq!(std::mem::size_of::<zlong>(), 8);
5009        assert_eq!(std::mem::size_of::<zulong>(), 8);
5010    }
5011
5012    #[test]
5013    fn meta_byte_value() {
5014        let _g = crate::test_util::global_state_lock();
5015        assert_eq!(Meta as u32, 0x83);
5016    }
5017
5018    #[test]
5019    fn parser_tokens_correct() {
5020        let _g = crate::test_util::global_state_lock();
5021        assert_eq!(crate::token_char::token_byte(Pound), Some(0x84));
5022        assert_eq!(crate::token_char::token_byte(Bang), Some(0x9c));
5023        assert_eq!(crate::token_char::token_byte(Snull), Some(0x9d));
5024        assert_eq!(crate::token_char::token_byte(Dnull), Some(0x9e));
5025        assert_eq!(crate::token_char::token_byte(Bnull), Some(0x9f));
5026        assert_eq!(crate::token_char::token_byte(Bnullkeep), Some(0xa0));
5027        assert_eq!(crate::token_char::token_byte(Nularg), Some(0xa1));
5028        assert_eq!(crate::token_char::token_byte(Marker), Some(0xa2));
5029    }
5030
5031    #[test]
5032    fn pm_type_isolates_type_bits() {
5033        let _g = crate::test_util::global_state_lock();
5034        assert_eq!(PM_TYPE(PM_INTEGER | PM_EXPORTED), PM_INTEGER);
5035        assert_eq!(PM_TYPE(PM_ARRAY | PM_READONLY), PM_ARRAY);
5036    }
5037
5038    #[test]
5039    fn opt_isset_basic() {
5040        let _g = crate::test_util::global_state_lock();
5041        let mut ops = options {
5042            ind: [0u8; MAX_OPS],
5043            args: Vec::new(),
5044            argscount: 0,
5045            argsalloc: 0,
5046        };
5047        ops.ind[b'l' as usize] = 1; // OPT_MINUS bit
5048        assert!(OPT_ISSET(&ops, b'l'));
5049        assert!(OPT_MINUS(&ops, b'l'));
5050        assert!(!OPT_PLUS(&ops, b'l'));
5051        assert!(!OPT_ISSET(&ops, b'r'));
5052    }
5053
5054    #[test]
5055    fn binf_constants_correct() {
5056        let _g = crate::test_util::global_state_lock();
5057        assert_eq!(BINF_PREFIX, 1 << 5);
5058        assert_eq!(BINF_ASSIGN, 1 << 19);
5059    }
5060
5061    #[test]
5062    fn cond_constants_correct() {
5063        let _g = crate::test_util::global_state_lock();
5064        assert_eq!(COND_NOT, 0);
5065        assert_eq!(COND_MODI, 19);
5066    }
5067
5068    #[test]
5069    fn fdt_constants_correct() {
5070        let _g = crate::test_util::global_state_lock();
5071        assert_eq!(FDT_UNUSED, 0);
5072        assert_eq!(FDT_PROC_SUBST, 7);
5073        assert_eq!(FDT_TYPE_MASK, 15);
5074    }
5075
5076    #[test]
5077    fn redir_iswrite_classification() {
5078        let _g = crate::test_util::global_state_lock();
5079        assert!(IS_WRITE_FILE(REDIR_WRITE));
5080        assert!(IS_WRITE_FILE(REDIR_READWRITE));
5081        assert!(!IS_WRITE_FILE(REDIR_READ));
5082        assert!(IS_ERROR_REDIR(REDIR_ERRWRITE));
5083        assert!(IS_ERROR_REDIR(REDIR_ERRAPPNOW));
5084        assert!(!IS_ERROR_REDIR(REDIR_WRITE));
5085    }
5086
5087    #[test]
5088    fn wc_macros_round_trip() {
5089        let _g = crate::test_util::global_state_lock();
5090        let w = wc_bld(WC_LIST, 42);
5091        assert_eq!(wc_code(w), WC_LIST);
5092        assert_eq!(wc_data(w), 42);
5093    }
5094
5095    #[test]
5096    fn mb_metastrlen_counts_meta_pairs() {
5097        let _g = crate::test_util::global_state_lock();
5098        assert_eq!(MB_METASTRLEN("abc"), 3);
5099        assert_eq!(MB_METASTRLEN("hello"), 5);
5100        assert_eq!(MB_METASTRLEN(""), 0);
5101        // The ASCII cases above are the only thing this test used to
5102        // assert, under a name promising meta-pair coverage — they pass
5103        // identically whether the body counts characters or bytes, so
5104        // they pinned nothing. A `Meta`+byte pair is ONE character
5105        // (c:Src/utils.c:5672-5673 un-metafies before counting).
5106        assert_eq!(
5107            MB_METASTRLEN("\u{83}\u{c6}\u{83}\u{b7}\u{83}\u{85}"),
5108            1,
5109            "metafied e6 97 a5 is the single character 日"
5110        );
5111        // A real multibyte character counts once, not once per byte.
5112        assert_eq!(MB_METASTRLEN("日本語"), 3);
5113        assert_eq!(MB_METASTRLEN("aé漢z"), 4);
5114    }
5115
5116    /// c:Src/zsh.h:3280 — `MB_METASTRWIDTH` is display COLUMNS, which
5117    /// is a different number from `MB_METASTRLEN` exactly when the
5118    /// string holds wide or combining characters. It aliased
5119    /// `MB_METASTRLEN` before, so this discriminates the two.
5120    #[test]
5121    fn mb_metastrwidth_counts_display_columns_not_chars() {
5122        let _g = crate::test_util::global_state_lock();
5123        assert_eq!(MB_METASTRLEN("日本語"), 3, "three characters");
5124        assert_eq!(MB_METASTRWIDTH("日本語"), 6, "six columns — each is wide");
5125        // Combining acute occupies no column of its own.
5126        assert_eq!(MB_METASTRLEN("e\u{301}"), 2);
5127        assert_eq!(MB_METASTRWIDTH("e\u{301}"), 1);
5128        // Pure ASCII: the two agree, which is why ASCII-only cases
5129        // cannot tell a width implementation from a length one.
5130        assert_eq!(MB_METASTRWIDTH("abc"), 3);
5131    }
5132
5133    #[test]
5134    fn mb_charlen_basic() {
5135        let _g = crate::test_util::global_state_lock();
5136        assert_eq!(MB_CHARLEN(b"abc", 3), 1);
5137        assert_eq!(MB_CHARLEN(b"", 0), 0);
5138    }
5139
5140    #[test]
5141    fn wcwidth_basic() {
5142        let _g = crate::test_util::global_state_lock();
5143        assert_eq!(WCWIDTH('a'), 1);
5144        // wcwidth(3): control chars are -1, NOT 0 — the old `0` pin
5145        // documented a divergence that made IS_COMBINING('\n') true
5146        // (refresh build swallowed hard newlines into combining
5147        // clusters, flattening multiline buffers to one video row).
5148        assert_eq!(WCWIDTH('\u{0007}'), -1); // BEL is control
5149        assert_eq!(WCWIDTH('\n'), -1); // newline is control
5150        assert!(
5151            !IS_COMBINING('\n'),
5152            "hard newline is never a combining mark"
5153        );
5154        assert_eq!(WCWIDTH('\u{4E2D}'), 2); // CJK
5155    }
5156
5157    #[test]
5158    fn is_combining_zero_width() {
5159        let _g = crate::test_util::global_state_lock();
5160        assert!(!IS_COMBINING('a')); // width 1
5161        assert!(!IS_COMBINING('\u{0000}')); // null returns false per c:3343
5162                                            // Control chars are wcwidth -1 per wcwidth(3), so IS_COMBINING
5163                                            // (`WCWIDTH(wc) == 0`, c:3343) is FALSE for them — the old pin
5164                                            // asserted the width-0 divergence that made the refresh build
5165                                            // absorb '\n'/BEL into combining clusters.
5166        assert!(!IS_COMBINING('\u{0007}'));
5167        // Real combining mark (unicode-width table): width 0 → true.
5168        assert!(IS_COMBINING('\u{0301}'));
5169    }
5170
5171    #[test]
5172    fn pat_flags_correct() {
5173        let _g = crate::test_util::global_state_lock();
5174        assert_eq!(PAT_FILE, 0x0001);
5175        assert_eq!(PAT_LCMATCHUC, 0x1000);
5176    }
5177
5178    #[test]
5179    fn sub_flags_correct() {
5180        let _g = crate::test_util::global_state_lock();
5181        assert_eq!(SUB_END, 0x0001);
5182        assert_eq!(SUB_EGLOB, 0x4000);
5183    }
5184
5185    #[test]
5186    fn pp_constants_ordered() {
5187        let _g = crate::test_util::global_state_lock();
5188        assert_eq!(PP_FIRST, PP_ALPHA);
5189        assert!(PP_LAST >= PP_ALPHA);
5190        assert!(PP_RANGE > PP_LAST);
5191    }
5192
5193    #[test]
5194    fn typeset_optstr_constants() {
5195        let _g = crate::test_util::global_state_lock();
5196        assert_eq!(TYPESET_OPTSTR, "aiEFALRZlurtxUhHT");
5197        assert_eq!(TYPESET_OPTNUM, "LRZiEF");
5198    }
5199
5200    #[test]
5201    fn job_stat_flags_distinct() {
5202        let _g = crate::test_util::global_state_lock();
5203        let all = STAT_CHANGED
5204            | STAT_STOPPED
5205            | STAT_TIMED
5206            | STAT_DONE
5207            | STAT_LOCKED
5208            | STAT_NOPRINT
5209            | STAT_INUSE
5210            | STAT_SUPERJOB
5211            | STAT_SUBJOB
5212            | STAT_WASSUPER
5213            | STAT_CURSH
5214            | STAT_NOSTTY
5215            | STAT_ATTACH
5216            | STAT_SUBLEADER
5217            | STAT_BUILTIN
5218            | STAT_SUBJOB_ORPHANED
5219            | STAT_DISOWN;
5220        assert_eq!(all.count_ones(), 17);
5221    }
5222
5223    #[test]
5224    fn opt_size_at_185() {
5225        let _g = crate::test_util::global_state_lock();
5226        assert_eq!(OPT_SIZE, 185);
5227    }
5228
5229    #[test]
5230    fn cs_count_is_32() {
5231        let _g = crate::test_util::global_state_lock();
5232        assert_eq!(CS_COUNT, 32);
5233    }
5234
5235    #[test]
5236    fn zwc_passes_through() {
5237        let _g = crate::test_util::global_state_lock();
5238        assert_eq!(ZWC('a'), 'a');
5239    }
5240
5241    /// `IS_DASH` matches both ASCII '-' (0x2D) and the tokenized
5242    /// Dash marker (0x9B) the lexer emits inside `[[ ... ]]`. Both
5243    /// must be recognised — regression that drops one would break
5244    /// either user-typed input OR pre-lexed cond expressions.
5245    #[test]
5246    fn is_dash_recognises_both_ascii_and_lexed_token() {
5247        let _g = crate::test_util::global_state_lock();
5248        assert!(IS_DASH('-'), "ASCII '-' is dash");
5249        assert!(IS_DASH('\u{e19b}'), "lexed Dash token is dash");
5250        assert!(!IS_DASH('+'), "non-dash chars must NOT match");
5251        assert!(!IS_DASH(' '), "space is not dash");
5252    }
5253
5254    /// c:1408 — `OPT_ISSET(ops, c)` returns true iff `ops.ind[c] != 0`.
5255    /// Catches a regression where the indexing returns NUL-byte
5256    /// "false" for a set option, breaking every `-x` flag check.
5257    #[test]
5258    fn opt_isset_reads_ind_array_directly() {
5259        let _g = crate::test_util::global_state_lock();
5260        let mut ops = options {
5261            ind: [0u8; MAX_OPS],
5262            args: Vec::new(),
5263            argscount: 0,
5264            argsalloc: 0,
5265        };
5266        assert!(!OPT_ISSET(&ops, b'x'));
5267        ops.ind[b'x' as usize] = 1;
5268        assert!(
5269            OPT_ISSET(&ops, b'x'),
5270            "after setting ind, OPT_ISSET must be true"
5271        );
5272        ops.ind[b'x' as usize] = 0;
5273        assert!(
5274            !OPT_ISSET(&ops, b'x'),
5275            "clearing ind must make OPT_ISSET false"
5276        );
5277    }
5278
5279    /// `PM_TYPE` masks just the type-bits of a flag word (PM_SCALAR /
5280    /// PM_INTEGER / PM_ARRAY / PM_HASHED / etc.). Modifier flags
5281    /// (PM_READONLY, PM_EXPORTED) MUST NOT leak through. A regression
5282    /// returning the full flag word would silently mis-dispatch every
5283    /// param introspection path.
5284    #[test]
5285    fn pm_type_strips_modifier_flags() {
5286        let _g = crate::test_util::global_state_lock();
5287        let with_mods = PM_INTEGER | PM_READONLY | PM_EXPORTED;
5288        assert_eq!(PM_TYPE(with_mods), PM_INTEGER);
5289        let just_array = PM_TYPE(PM_ARRAY | PM_LEFT | PM_TIED);
5290        assert_eq!(just_array, PM_ARRAY);
5291    }
5292
5293    /// `Src/zsh.h:1878-1944` — `PM_*` flag values are load-bearing.
5294    /// Pin EVERY parameter-mode flag against the canonical C define.
5295    /// If any value drifts, serialised param state (typeset, export,
5296    /// hash dumps) corrupts on the next read.
5297    #[test]
5298    fn pm_flags_match_c_zsh_h_canonical_values() {
5299        let _g = crate::test_util::global_state_lock();
5300        assert_eq!(PM_SCALAR, 0, "c:1878");
5301        assert_eq!(PM_ARRAY, 1 << 0, "c:1879");
5302        assert_eq!(PM_INTEGER, 1 << 1, "c:1880");
5303        assert_eq!(PM_EFLOAT, 1 << 2, "c:1881");
5304        assert_eq!(PM_FFLOAT, 1 << 3, "c:1882");
5305        assert_eq!(PM_HASHED, 1 << 4, "c:1883");
5306        assert_eq!(PM_LEFT, 1 << 5, "c:1888");
5307        assert_eq!(PM_RIGHT_B, 1 << 6, "c:1889");
5308        assert_eq!(PM_RIGHT_Z, 1 << 7, "c:1890");
5309        assert_eq!(PM_LOWER, 1 << 8, "c:1891");
5310        assert_eq!(PM_UPPER, 1 << 9, "c:1895");
5311        assert_eq!(PM_UNDEFINED, 1 << 9, "c:1896 (aliases PM_UPPER for funcs)");
5312        assert_eq!(PM_READONLY, 1 << 10, "c:1898");
5313        assert_eq!(PM_TAGGED, 1 << 11, "c:1899");
5314        assert_eq!(PM_EXPORTED, 1 << 12, "c:1900");
5315        assert_eq!(
5316            PM_ABSPATH_USED,
5317            1 << 12,
5318            "c:1901 (aliases EXPORTED for funcs)"
5319        );
5320        assert_eq!(PM_UNIQUE, 1 << 13, "c:1905");
5321        assert_eq!(PM_HIDE, 1 << 14, "c:1908");
5322        assert_eq!(PM_HIDEVAL, 1 << 15, "c:1910");
5323        assert_eq!(PM_TIED, 1 << 16, "c:1912");
5324        assert_eq!(PM_SPECIAL, 1 << 20, "c:1922");
5325        assert_eq!(PM_RO_BY_DESIGN, 1 << 21, "c:1924");
5326        assert_eq!(PM_LOCAL, 1 << 19, "c:1920");
5327        assert_eq!(PM_UNSET, 1 << 24, "c:1930");
5328        assert_eq!(PM_NAMEREF, 1 << 30, "c:1944");
5329    }
5330
5331    /// `Src/zsh.h:1953-1965` — `SCANPM_*` flag values for parameter
5332    /// scanning. Used by `${(k)hash}` / `${(K)hash}` / `${(m)hash}`
5333    /// expansion paths. Drift here mis-routes every hash scan.
5334    #[test]
5335    fn scanpm_flags_match_c_zsh_h_canonical_values() {
5336        let _g = crate::test_util::global_state_lock();
5337        assert_eq!(SCANPM_WANTVALS, 1 << 0, "c:1953");
5338        assert_eq!(SCANPM_WANTKEYS, 1 << 1, "c:1954");
5339        assert_eq!(SCANPM_WANTINDEX, 1 << 2, "c:1955");
5340        assert_eq!(SCANPM_MATCHKEY, 1 << 3, "c:1956");
5341        assert_eq!(SCANPM_MATCHVAL, 1 << 4, "c:1957");
5342        assert_eq!(SCANPM_MATCHMANY, 1 << 5, "c:1958");
5343        assert_eq!(SCANPM_ASSIGNING, 1 << 6, "c:1959");
5344        assert_eq!(SCANPM_KEYMATCH, 1 << 7, "c:1960");
5345        assert_eq!(SCANPM_DQUOTED, 1 << 8, "c:1961");
5346        assert_eq!(SCANPM_ARRONLY, 1 << 9, "c:1965");
5347    }
5348
5349    /// `Src/zsh.h:144-224` — parser token byte values. These are
5350    /// **load-bearing single-byte sentinels** for every lex/parse
5351    /// path (Pound for `#` comments, Star for `*` glob, Stringg for
5352    /// `$param`, etc.). A drift on any byte would silently mis-route
5353    /// every parsed token.
5354    #[test]
5355    fn token_byte_values_match_c_zsh_h() {
5356        let _g = crate::test_util::global_state_lock();
5357        assert_eq!(Meta, 0x83, "c:144");
5358        assert_eq!(Pound, '\u{e184}', "c:159");
5359        assert_eq!(Stringg, '\u{e185}', "c:160");
5360        assert_eq!(Hat, '\u{e186}', "c:161");
5361        assert_eq!(Star, '\u{e187}', "c:162");
5362        assert_eq!(Inpar, '\u{e188}', "c:163");
5363        assert_eq!(Inparmath, '\u{e189}', "c:164");
5364        assert_eq!(Outpar, '\u{e18a}', "c:165");
5365        assert_eq!(Outparmath, '\u{e18b}', "c:166");
5366        assert_eq!(Qstring, '\u{e18c}', "c:167");
5367        assert_eq!(Equals, '\u{e18d}', "c:168");
5368        assert_eq!(Bar, '\u{e18e}', "c:169");
5369        assert_eq!(Inbrace, '\u{e18f}', "c:170");
5370        assert_eq!(Outbrace, '\u{e190}', "c:171");
5371        assert_eq!(Inbrack, '\u{e191}', "c:172");
5372        assert_eq!(Outbrack, '\u{e192}', "c:173");
5373        assert_eq!(Tick, '\u{e193}', "c:174");
5374        assert_eq!(Inang, '\u{e194}', "c:175");
5375        assert_eq!(Outang, '\u{e195}', "c:176");
5376        assert_eq!(OutangProc, '\u{e196}', "c:177");
5377        assert_eq!(Quest, '\u{e197}', "c:178");
5378        assert_eq!(Tilde, '\u{e198}', "c:179");
5379        assert_eq!(Qtick, '\u{e199}', "c:180");
5380        assert_eq!(Comma, '\u{e19a}', "c:181");
5381        assert_eq!(Dash, '\u{e19b}', "c:182");
5382        assert_eq!(Bang, '\u{e19c}', "c:183");
5383        assert_eq!(LAST_NORMAL_TOK, Bang, "c:188 == Bang");
5384        assert_eq!(Snull, '\u{e19d}', "c:193");
5385        assert_eq!(Dnull, '\u{e19e}', "c:194");
5386        assert_eq!(Bnull, '\u{e19f}', "c:195");
5387        assert_eq!(Bnullkeep, '\u{e1a0}', "c:200");
5388        assert_eq!(Nularg, '\u{e1a1}', "c:206");
5389        assert_eq!(Marker, '\u{e1a2}', "c:224");
5390    }
5391
5392    /// `Src/zsh.h:226-232` — SPECCHARS / PATCHARS string literals.
5393    /// Pin the exact 25-char SPECCHARS set and 13-char PATCHARS set
5394    /// against the canonical C define. Drift on either would silently
5395    /// change which chars trigger quoting / pattern matching.
5396    #[test]
5397    fn specchars_patchars_match_c_zsh_h() {
5398        let _g = crate::test_util::global_state_lock();
5399        // c:228 — `SPECCHARS "#$^*()=|{}[]`<>?~;&\n\t \\\'\""`.
5400        assert_eq!(
5401            SPECCHARS, "#$^*()=|{}[]`<>?~;&\n\t \\\'\"",
5402            "c:228 — SPECCHARS literal must match C verbatim"
5403        );
5404        assert_eq!(
5405            SPECCHARS.chars().count(),
5406            25,
5407            "c:228 — SPECCHARS has 25 chars"
5408        );
5409        // c:232 — `PATCHARS "#^*()|[]<>?~\\"`.
5410        assert_eq!(
5411            PATCHARS, "#^*()|[]<>?~\\",
5412            "c:232 — PATCHARS literal must match C verbatim"
5413        );
5414        assert_eq!(
5415            PATCHARS.chars().count(),
5416            13,
5417            "c:232 — PATCHARS has 13 chars"
5418        );
5419    }
5420
5421    /// `Src/zsh.h:149-153` — DEFAULT_IFS and DEFAULT_IFS_SH literals.
5422    #[test]
5423    fn default_ifs_strings_match_c_zsh_h() {
5424        let _g = crate::test_util::global_state_lock();
5425        // c:149 — `DEFAULT_IFS " \t\n\203 "` (5 chars; \203 is Meta).
5426        assert_eq!(
5427            DEFAULT_IFS, " \t\n\u{83} ",
5428            "c:149 — DEFAULT_IFS = space + tab + newline + Meta + space"
5429        );
5430        assert_eq!(
5431            DEFAULT_IFS.chars().count(),
5432            5,
5433            "c:149 — DEFAULT_IFS is 5 chars"
5434        );
5435        // c:153 — `DEFAULT_IFS_SH " \t\n"` (3 chars, POSIX sh).
5436        assert_eq!(
5437            DEFAULT_IFS_SH, " \t\n",
5438            "c:153 — DEFAULT_IFS_SH = POSIX 3-char set"
5439        );
5440    }
5441
5442    /// `Src/zsh.h:1879-1883` — `PM_TYPE_MASK` covers the 5 type bits
5443    /// (PM_ARRAY..PM_HASHED). Every non-type flag must be OUTSIDE
5444    /// this mask. Pin so a regression that bleeds modifier flags
5445    /// into the type space fails.
5446    #[test]
5447    fn pm_type_mask_excludes_modifier_flags() {
5448        let _g = crate::test_util::global_state_lock();
5449        let type_mask = PM_ARRAY | PM_INTEGER | PM_EFLOAT | PM_FFLOAT | PM_HASHED;
5450        // Modifier flags MUST be outside the type-mask range.
5451        for modifier in &[
5452            PM_LEFT,
5453            PM_RIGHT_B,
5454            PM_RIGHT_Z,
5455            PM_LOWER,
5456            PM_UPPER,
5457            PM_READONLY,
5458            PM_EXPORTED,
5459            PM_LOCAL,
5460            PM_UNSET,
5461        ] {
5462            assert_eq!(
5463                modifier & type_mask,
5464                0,
5465                "modifier flag 0x{:x} must NOT overlap type mask 0x{:x}",
5466                modifier,
5467                type_mask
5468            );
5469        }
5470    }
5471
5472    /// `Src/zsh.h` IS_WRITE_FILE — `X >= REDIR_WRITE && X <= REDIR_READWRITE`
5473    /// (0..=8). Comprehensive sweep: every redir-type ID 0..=17 plus
5474    /// boundaries. A regression that flips the comparison would break
5475    /// every `>file` / `>>file` / `1>file` redirection emit.
5476    #[test]
5477    fn is_write_file_sweep_all_redir_types() {
5478        let _g = crate::test_util::global_state_lock();
5479        // c:298-299 — write-file family: 0..=8 inclusive.
5480        for x in REDIR_WRITE..=REDIR_READWRITE {
5481            assert!(
5482                IS_WRITE_FILE(x),
5483                "redir-type {} ({}) must be IS_WRITE_FILE",
5484                x,
5485                x
5486            );
5487        }
5488        // Outside the range — NOT write-file.
5489        for x in [
5490            REDIR_READ,
5491            REDIR_HEREDOC,
5492            REDIR_HEREDOCDASH,
5493            REDIR_HERESTR,
5494            REDIR_MERGEIN,
5495            REDIR_MERGEOUT,
5496            REDIR_CLOSE,
5497            REDIR_INPIPE,
5498            REDIR_OUTPIPE,
5499        ] {
5500            assert!(
5501                !IS_WRITE_FILE(x),
5502                "redir-type {} must NOT be IS_WRITE_FILE",
5503                x
5504            );
5505        }
5506    }
5507
5508    /// `Src/zsh.h` IS_APPEND_REDIR — `IS_WRITE_FILE && (X & 2)`. Bit 1
5509    /// distinguishes write (0/1/4/5) from append (2/3/6/7). Pin every
5510    /// boundary so a regression that masks the wrong bit silently
5511    /// dispatches `>file` as `>>file`.
5512    #[test]
5513    fn is_append_redir_pins_bit_1() {
5514        let _g = crate::test_util::global_state_lock();
5515        // c:303-304 — true ONLY for 2/3/6/7 in the write-file family.
5516        assert!(IS_APPEND_REDIR(REDIR_APP), "REDIR_APP=2 is append");
5517        assert!(IS_APPEND_REDIR(REDIR_APPNOW), "REDIR_APPNOW=3 is append");
5518        assert!(IS_APPEND_REDIR(REDIR_ERRAPP), "REDIR_ERRAPP=6 is append");
5519        assert!(
5520            IS_APPEND_REDIR(REDIR_ERRAPPNOW),
5521            "REDIR_ERRAPPNOW=7 is append"
5522        );
5523        // NOT append.
5524        assert!(!IS_APPEND_REDIR(REDIR_WRITE), "REDIR_WRITE=0 is not append");
5525        assert!(
5526            !IS_APPEND_REDIR(REDIR_WRITENOW),
5527            "REDIR_WRITENOW=1 is not append"
5528        );
5529        assert!(
5530            !IS_APPEND_REDIR(REDIR_ERRWRITE),
5531            "REDIR_ERRWRITE=4 is not append"
5532        );
5533        assert!(
5534            !IS_APPEND_REDIR(REDIR_ERRWRITENOW),
5535            "REDIR_ERRWRITENOW=5 is not append"
5536        );
5537        // Outside the write-file family — never append.
5538        assert!(
5539            !IS_APPEND_REDIR(REDIR_READ),
5540            "REDIR_READ=9 is not write-file"
5541        );
5542    }
5543
5544    /// `Src/zsh.h` IS_CLOBBER_REDIR — `IS_WRITE_FILE && (X & 1)`. Bit 0
5545    /// is the `NOW` suffix (>! / >>!) that bypasses NO_CLOBBER. Pin
5546    /// boundary so a regression mis-flagging clobber would break user
5547    /// scripts that rely on NO_CLOBBER semantics.
5548    #[test]
5549    fn is_clobber_redir_pins_bit_0() {
5550        let _g = crate::test_util::global_state_lock();
5551        // c:308-309 — true ONLY for 1/3/5/7 in the write-file family.
5552        assert!(
5553            IS_CLOBBER_REDIR(REDIR_WRITENOW),
5554            "REDIR_WRITENOW=1 is clobber"
5555        );
5556        assert!(IS_CLOBBER_REDIR(REDIR_APPNOW), "REDIR_APPNOW=3 is clobber");
5557        assert!(
5558            IS_CLOBBER_REDIR(REDIR_ERRWRITENOW),
5559            "REDIR_ERRWRITENOW=5 is clobber"
5560        );
5561        assert!(
5562            IS_CLOBBER_REDIR(REDIR_ERRAPPNOW),
5563            "REDIR_ERRAPPNOW=7 is clobber"
5564        );
5565        // NOT clobber.
5566        assert!(!IS_CLOBBER_REDIR(REDIR_WRITE));
5567        assert!(!IS_CLOBBER_REDIR(REDIR_APP));
5568        assert!(!IS_CLOBBER_REDIR(REDIR_ERRWRITE));
5569        assert!(!IS_CLOBBER_REDIR(REDIR_ERRAPP));
5570    }
5571
5572    /// `Src/zsh.h` IS_ERROR_REDIR — `X >= REDIR_ERRWRITE && X <=
5573    /// REDIR_ERRAPPNOW` (4..=7). Pin both boundaries — a regression
5574    /// flipping `<=` to `<` would silently exclude REDIR_ERRAPPNOW.
5575    #[test]
5576    fn is_error_redir_inclusive_range() {
5577        let _g = crate::test_util::global_state_lock();
5578        // c:313-314 — true ONLY for 4..=7.
5579        for x in REDIR_ERRWRITE..=REDIR_ERRAPPNOW {
5580            assert!(IS_ERROR_REDIR(x), "redir-type {} must be IS_ERROR_REDIR", x);
5581        }
5582        // Outside — never error.
5583        for x in [
5584            REDIR_WRITE,
5585            REDIR_WRITENOW,
5586            REDIR_APP,
5587            REDIR_APPNOW,
5588            REDIR_READWRITE,
5589            REDIR_READ,
5590            REDIR_HEREDOC,
5591            REDIR_INPIPE,
5592        ] {
5593            assert!(
5594                !IS_ERROR_REDIR(x),
5595                "redir-type {} must NOT be IS_ERROR_REDIR",
5596                x
5597            );
5598        }
5599    }
5600
5601    /// `Src/zsh.h` IS_READFD — `(X >= REDIR_READWRITE && X <= REDIR_MERGEIN)
5602    /// || X == REDIR_INPIPE`. Read-fd family: 8..=13 OR 16. Pin so a
5603    /// regression that drops the OR INPIPE arm breaks process
5604    /// substitution `<(cmd)` redirections.
5605    #[test]
5606    fn is_readfd_range_plus_inpipe() {
5607        let _g = crate::test_util::global_state_lock();
5608        // c:318-319 — true for 8..=13 INCLUSIVE plus INPIPE=16.
5609        for x in REDIR_READWRITE..=REDIR_MERGEIN {
5610            assert!(IS_READFD(x), "redir-type {} must be IS_READFD", x);
5611        }
5612        assert!(
5613            IS_READFD(REDIR_INPIPE),
5614            "REDIR_INPIPE=16 must be IS_READFD (special-case OR arm)"
5615        );
5616        // Outside — not readfd.
5617        assert!(!IS_READFD(REDIR_WRITE));
5618        assert!(!IS_READFD(REDIR_MERGEOUT));
5619        assert!(!IS_READFD(REDIR_CLOSE));
5620        assert!(!IS_READFD(REDIR_OUTPIPE));
5621    }
5622
5623    /// `Src/zsh.h:273-290` — REDIR_* numeric values are load-bearing
5624    /// because IS_APPEND/IS_CLOBBER use bit-arithmetic on the values.
5625    /// Pin EVERY entry's exact value so a reorder silently flips the
5626    /// append-vs-write classification across the parser + executor.
5627    #[test]
5628    fn redir_constants_have_exact_canonical_values() {
5629        let _g = crate::test_util::global_state_lock();
5630        assert_eq!(REDIR_WRITE, 0);
5631        assert_eq!(REDIR_WRITENOW, 1);
5632        assert_eq!(REDIR_APP, 2);
5633        assert_eq!(REDIR_APPNOW, 3);
5634        assert_eq!(REDIR_ERRWRITE, 4);
5635        assert_eq!(REDIR_ERRWRITENOW, 5);
5636        assert_eq!(REDIR_ERRAPP, 6);
5637        assert_eq!(REDIR_ERRAPPNOW, 7);
5638        assert_eq!(REDIR_READWRITE, 8);
5639        assert_eq!(REDIR_READ, 9);
5640        assert_eq!(REDIR_HEREDOC, 10);
5641        assert_eq!(REDIR_HEREDOCDASH, 11);
5642        assert_eq!(REDIR_HERESTR, 12);
5643        assert_eq!(REDIR_MERGEIN, 13);
5644        assert_eq!(REDIR_MERGEOUT, 14);
5645        assert_eq!(REDIR_CLOSE, 15);
5646        assert_eq!(REDIR_INPIPE, 16);
5647        assert_eq!(REDIR_OUTPIPE, 17);
5648    }
5649
5650    /// `Src/zsh.h` REDIR_TYPE_MASK / REDIR_VARID_MASK /
5651    /// REDIR_FROM_HEREDOC_MASK — these encode redir flags in the
5652    /// wordcode redir entry. The type-mask MUST be 0x1f (5 bits) so
5653    /// it covers REDIR_OUTPIPE=17 (0x11) without overlapping the
5654    /// 0x20/0x40 flag bits.
5655    #[test]
5656    fn redir_masks_have_no_overlap() {
5657        let _g = crate::test_util::global_state_lock();
5658        assert_eq!(REDIR_TYPE_MASK, 0x1f);
5659        assert_eq!(REDIR_VARID_MASK, 0x20);
5660        assert_eq!(REDIR_FROM_HEREDOC_MASK, 0x40);
5661        // Masks must be pairwise disjoint.
5662        assert_eq!(REDIR_TYPE_MASK & REDIR_VARID_MASK, 0);
5663        assert_eq!(REDIR_TYPE_MASK & REDIR_FROM_HEREDOC_MASK, 0);
5664        assert_eq!(REDIR_VARID_MASK & REDIR_FROM_HEREDOC_MASK, 0);
5665        // Type-mask must cover the largest REDIR_* value (17 = 0x11).
5666        assert_eq!(
5667            REDIR_OUTPIPE & REDIR_TYPE_MASK,
5668            REDIR_OUTPIPE,
5669            "type-mask must include every REDIR_* up to OUTPIPE=17"
5670        );
5671    }
5672
5673    // ─── WCWIDTH / IS_COMBINING / IS_BASECHAR pin tests ──────────────
5674
5675    /// `WCWIDTH('a')` = 1 (basic ASCII single width).
5676    #[test]
5677    fn zshh_corpus_wcwidth_ascii_is_one() {
5678        assert_eq!(WCWIDTH('a'), 1);
5679        assert_eq!(WCWIDTH('Z'), 1);
5680        assert_eq!(WCWIDTH('0'), 1);
5681        assert_eq!(WCWIDTH(' '), 1);
5682    }
5683
5684    /// `WCWIDTH` on common CJK char is 2 (East Asian Wide).
5685    #[test]
5686    fn zshh_corpus_wcwidth_cjk_is_two() {
5687        // U+4E2D '中' (Chinese for middle) — wide.
5688        assert_eq!(WCWIDTH('中'), 2);
5689        // U+65E5 '日' (Japanese for day) — wide.
5690        assert_eq!(WCWIDTH('日'), 2);
5691    }
5692
5693    /// `WCWIDTH` on combining mark is 0.
5694    /// U+0301 = COMBINING ACUTE ACCENT.
5695    #[test]
5696    fn zshh_corpus_wcwidth_combining_is_zero() {
5697        assert_eq!(WCWIDTH('\u{0301}'), 0, "combining acute accent has width 0");
5698    }
5699
5700    /// `IS_COMBINING` true for combining accent codepoints.
5701    #[test]
5702    fn zshh_corpus_is_combining_true_for_accents() {
5703        assert!(IS_COMBINING('\u{0301}'), "U+0301 COMBINING ACUTE");
5704        assert!(IS_COMBINING('\u{0300}'), "U+0300 COMBINING GRAVE");
5705    }
5706
5707    /// `IS_COMBINING` false for normal ASCII letters.
5708    #[test]
5709    fn zshh_corpus_is_combining_false_for_ascii() {
5710        assert!(!IS_COMBINING('a'));
5711        assert!(!IS_COMBINING('Z'));
5712        assert!(!IS_COMBINING('0'));
5713    }
5714
5715    /// `IS_BASECHAR` true for ordinary printable chars.
5716    #[test]
5717    fn zshh_corpus_is_basechar_true_for_letters() {
5718        assert!(IS_BASECHAR('a'));
5719        assert!(IS_BASECHAR('A'));
5720        assert!(IS_BASECHAR('z'));
5721        assert!(IS_BASECHAR('日'));
5722    }
5723
5724    /// `IS_BASECHAR` false for combining marks.
5725    #[test]
5726    fn zshh_corpus_is_basechar_false_for_combining() {
5727        assert!(
5728            !IS_BASECHAR('\u{0301}'),
5729            "combining accent is not a base char"
5730        );
5731    }
5732
5733    /// Pound's C byte (the lex marker) is in the imeta range 0x83..=0xa2.
5734    #[test]
5735    fn zshh_corpus_pound_marker_in_imeta_range() {
5736        let p = crate::token_char::token_byte(Pound).expect("Pound is a token char") as u32;
5737        assert!(
5738            p >= 0x83 && p <= 0xa2,
5739            "Pound = {:#x} must be in imeta range 0x83..=0xa2",
5740            p
5741        );
5742    }
5743
5744    // ═══════════════════════════════════════════════════════════════════
5745    // C-parity tests pinning Src/zsh.h macro ports.
5746    // ═══════════════════════════════════════════════════════════════════
5747
5748    /// `minimum(3, 5)` returns 3. C `#define minimum(x,y) ((x)<(y)?(x):(y))`.
5749    #[test]
5750    fn minimum_picks_smaller_int() {
5751        assert_eq!(minimum(3, 5), 3);
5752        assert_eq!(minimum(5, 3), 3);
5753    }
5754
5755    /// `minimum(3, 3)` returns either (both equal).
5756    #[test]
5757    fn minimum_equal_values_returns_value() {
5758        assert_eq!(minimum(7, 7), 7);
5759    }
5760
5761    /// `minimum(-5, 5)` returns -5 (negative smaller).
5762    #[test]
5763    fn minimum_negative_picks_negative() {
5764        assert_eq!(minimum(-5, 5), -5);
5765    }
5766
5767    /// `QT_IS_SINGLE(QT_SINGLE)` returns true.
5768    /// C `#define QT_IS_SINGLE(x) (x == QT_SINGLE || x == QT_SINGLE_OPTIONAL)`.
5769    #[test]
5770    fn QT_IS_SINGLE_recognises_QT_SINGLE() {
5771        assert!(QT_IS_SINGLE(QT_SINGLE));
5772    }
5773
5774    /// `QT_IS_SINGLE(QT_NONE)` returns false.
5775    #[test]
5776    fn QT_IS_SINGLE_QT_NONE_returns_false() {
5777        assert!(!QT_IS_SINGLE(QT_NONE));
5778    }
5779
5780    /// `IS_WRITE_FILE(REDIR_WRITE)` returns true. C `#define
5781    /// IS_WRITE_FILE(x)` — any write-redirect family.
5782    #[test]
5783    fn IS_WRITE_FILE_recognises_write_token() {
5784        assert!(IS_WRITE_FILE(REDIR_WRITE));
5785    }
5786
5787    /// `IS_APPEND_REDIR(REDIR_APP)` returns true.
5788    #[test]
5789    fn IS_APPEND_REDIR_recognises_app_token() {
5790        assert!(IS_APPEND_REDIR(REDIR_APP));
5791    }
5792
5793    /// `IS_ERROR_REDIR(REDIR_ERRWRITE)` returns true.
5794    #[test]
5795    fn IS_ERROR_REDIR_recognises_errwrite_token() {
5796        assert!(IS_ERROR_REDIR(REDIR_ERRWRITE));
5797    }
5798
5799    /// `IS_READFD(REDIR_READ)` returns true.
5800    #[test]
5801    fn IS_READFD_recognises_read_token() {
5802        assert!(IS_READFD(REDIR_READ));
5803    }
5804
5805    // ═══════════════════════════════════════════════════════════════════
5806    // Additional C-parity tests for Src/zsh.h wordcode + macro helpers.
5807    // ═══════════════════════════════════════════════════════════════════
5808
5809    /// c:918 — `WCB_END()` returns the WC_END opcode at offset 0.
5810    #[test]
5811    fn WCB_END_returns_wc_end_opcode() {
5812        let end = WCB_END();
5813        assert_eq!(wc_code(end), WC_END, "WCB_END encodes WC_END opcode");
5814        assert_eq!(wc_data(end), 0, "WCB_END encodes zero data");
5815    }
5816
5817    /// c:wc_bld — round-trip: wc_code(wc_bld(c, d)) == c.
5818    #[test]
5819    fn wc_bld_code_round_trip() {
5820        for opcode in [WC_END, WC_LIST, WC_SUBLIST, WC_PIPE] {
5821            let w = wc_bld(opcode, 42);
5822            assert_eq!(wc_code(w), opcode, "wc_code round-trips opcode {}", opcode);
5823        }
5824    }
5825
5826    /// c:wc_bld — round-trip data field: wc_data(wc_bld(c, d)) == d.
5827    #[test]
5828    fn wc_bld_data_round_trip() {
5829        for data in [0u32, 1, 42, 0xFFFF, 0x00FFFFFF] {
5830            let w = wc_bld(WC_LIST, data);
5831            assert_eq!(wc_data(w), data, "wc_data round-trips data {}", data);
5832        }
5833    }
5834
5835    /// c:920 — `WC_LIST_TYPE` reads the data field (matches wc_data).
5836    #[test]
5837    fn WC_LIST_TYPE_reads_data_field() {
5838        let w = WCB_LIST(7, 0);
5839        assert_eq!(WC_LIST_TYPE(w), 7, "list type round-trips");
5840    }
5841
5842    /// c:924 — `WC_LIST_SKIP` shifts down by WC_LIST_FREE bits.
5843    #[test]
5844    fn WC_LIST_SKIP_round_trip() {
5845        let w = WCB_LIST(0, 100);
5846        assert_eq!(WC_LIST_SKIP(w), 100, "list skip round-trips");
5847    }
5848
5849    /// c:927 — `WC_SUBLIST_TYPE` is data & 3 (low 2 bits).
5850    #[test]
5851    fn WC_SUBLIST_TYPE_masks_low_2_bits() {
5852        let w = WCB_SUBLIST(2, 0, 0); // type=2 fits in 2 bits
5853        assert_eq!(WC_SUBLIST_TYPE(w), 2);
5854    }
5855
5856    /// c:931 — `WC_SUBLIST_FLAGS` masks bits 2-4 (0x1c).
5857    #[test]
5858    fn WC_SUBLIST_FLAGS_masks_bits_2_through_4() {
5859        let w = WCB_SUBLIST(0, 0x04, 0); // flag bit 2
5860        assert_eq!(WC_SUBLIST_FLAGS(w), 0x04);
5861    }
5862
5863    /// c:940 — `WC_PIPE_TYPE` is data & 1 (low bit only).
5864    #[test]
5865    fn WC_PIPE_TYPE_masks_low_bit() {
5866        let w = WCB_PIPE(1, 100);
5867        assert_eq!(WC_PIPE_TYPE(w), 1);
5868        let w0 = WCB_PIPE(0, 100);
5869        assert_eq!(WC_PIPE_TYPE(w0), 0);
5870    }
5871
5872    /// c:940 — `WC_PIPE_LINENO` is data >> 1.
5873    #[test]
5874    fn WC_PIPE_LINENO_shifts_down_by_one() {
5875        let w = WCB_PIPE(0, 42);
5876        assert_eq!(WC_PIPE_LINENO(w), 42, "lineno round-trips through >> 1");
5877    }
5878
5879    /// c:215 — `IS_DASH('-')` returns true; other chars return false.
5880    #[test]
5881    fn IS_DASH_only_recognizes_hyphen() {
5882        assert!(IS_DASH('-'));
5883        assert!(!IS_DASH('+'));
5884        assert!(!IS_DASH(' '));
5885        assert!(!IS_DASH('a'));
5886        assert!(!IS_DASH('\0'));
5887    }
5888
5889    /// c:32 — `minimum(a, b)` returns the smaller of two values.
5890    #[test]
5891    fn minimum_returns_smaller_value() {
5892        assert_eq!(minimum(3, 7), 3);
5893        assert_eq!(minimum(7, 3), 3);
5894        assert_eq!(
5895            minimum(5, 5),
5896            5,
5897            "equal → either (returns first per PartialOrd)"
5898        );
5899        assert_eq!(minimum(-10, 10), -10);
5900    }
5901
5902    /// c:32 — minimum works with floats.
5903    #[test]
5904    fn minimum_works_with_floats() {
5905        assert_eq!(minimum(1.5_f64, 2.5_f64), 1.5);
5906        assert_eq!(minimum(-1.0_f64, 0.0_f64), -1.0);
5907    }
5908
5909    /// c:246 — `QT_IS_SINGLE(QT_SINGLE)` returns true.
5910    #[test]
5911    fn QT_IS_SINGLE_recognizes_single_quote() {
5912        // QT_SINGLE is the canonical single-quote token value.
5913        // Sanity: any non-QT_SINGLE returns false; QT_SINGLE → true.
5914        assert!(QT_IS_SINGLE(crate::ported::zsh_h::QT_SINGLE));
5915        assert!(!QT_IS_SINGLE(0));
5916        assert!(!QT_IS_SINGLE(-1));
5917    }
5918
5919    // ═══════════════════════════════════════════════════════════════════
5920    // Additional C-parity tests for Src/zsh.h wordcode WC_* helpers.
5921    // ═══════════════════════════════════════════════════════════════════
5922
5923    /// c:970 — `WCB_SIMPLE(N) / WC_SIMPLE_ARGC` round-trip preserves argc.
5924    #[test]
5925    fn WCB_SIMPLE_round_trips_argc() {
5926        for n in [0u32, 1, 42, 1000, 0xFFFF] {
5927            let w = WCB_SIMPLE(n);
5928            assert_eq!(WC_SIMPLE_ARGC(w), n, "argc {} must round-trip", n);
5929        }
5930    }
5931
5932    /// c:973 — `WCB_TYPESET / WC_TYPESET_ARGC` round-trip.
5933    #[test]
5934    fn WCB_TYPESET_round_trips_argc() {
5935        for n in [0u32, 5, 100, 1000] {
5936            let w = WCB_TYPESET(n);
5937            assert_eq!(WC_TYPESET_ARGC(w), n);
5938        }
5939    }
5940
5941    /// c:976 — `WCB_SUBSH / WC_SUBSH_SKIP` round-trip preserves skip offset.
5942    #[test]
5943    fn WCB_SUBSH_round_trips_skip() {
5944        for o in [0u32, 1, 100, 0x10000] {
5945            let w = WCB_SUBSH(o);
5946            assert_eq!(WC_SUBSH_SKIP(w), o);
5947        }
5948    }
5949
5950    /// c:979 — `WCB_CURSH / WC_CURSH_SKIP` round-trip.
5951    #[test]
5952    fn WCB_CURSH_round_trips_skip() {
5953        for o in [0u32, 1, 50, 500] {
5954            let w = WCB_CURSH(o);
5955            assert_eq!(WC_CURSH_SKIP(w), o);
5956        }
5957    }
5958
5959    /// c:982 — `WCB_TIMED / WC_TIMED_TYPE` round-trip.
5960    #[test]
5961    fn WCB_TIMED_round_trips_type() {
5962        for t in [0u32, 1, 2] {
5963            let w = WCB_TIMED(t);
5964            assert_eq!(WC_TIMED_TYPE(w), t);
5965        }
5966    }
5967
5968    /// c:987 — `WCB_FUNCDEF / WC_FUNCDEF_SKIP` round-trip.
5969    #[test]
5970    fn WCB_FUNCDEF_round_trips_skip() {
5971        for o in [0u32, 10, 100] {
5972            let w = WCB_FUNCDEF(o);
5973            assert_eq!(WC_FUNCDEF_SKIP(w), o);
5974        }
5975    }
5976
5977    /// c:990 — `WCB_FOR(type, skip)` encodes type in low 2 bits, skip
5978    /// in upper bits.
5979    #[test]
5980    fn WCB_FOR_packs_type_low_skip_high() {
5981        // type fits in 2 bits (0..3)
5982        for t in [0u32, 1, 2, 3] {
5983            for o in [0u32, 10, 100] {
5984                let w = WCB_FOR(t, o);
5985                assert_eq!(WC_FOR_TYPE(w), t, "type round-trip for ({}, {})", t, o);
5986                assert_eq!(WC_FOR_SKIP(w), o, "skip round-trip for ({}, {})", t, o);
5987            }
5988        }
5989    }
5990
5991    /// c:997 — `WC_SELECT_TYPE(c)` masks low bit only.
5992    #[test]
5993    fn WC_SELECT_TYPE_masks_low_bit() {
5994        // wc_bld with 0b11 in data → SELECT_TYPE = 1 (low bit).
5995        let w = wc_bld(WC_SELECT, 3);
5996        assert_eq!(WC_SELECT_TYPE(w), 1);
5997        let w0 = wc_bld(WC_SELECT, 4);
5998        assert_eq!(WC_SELECT_TYPE(w0), 0, "0b100 & 1 = 0");
5999    }
6000
6001    /// c:997 — `WC_SELECT_SKIP(c)` is data >> 1.
6002    #[test]
6003    fn WC_SELECT_SKIP_shifts_right_one() {
6004        let w = wc_bld(WC_SELECT, 42);
6005        assert_eq!(WC_SELECT_SKIP(w), 42 >> 1);
6006    }
6007
6008    /// c:990 — WC_FOR_TYPE only reads 2 bits — high bits ignored.
6009    #[test]
6010    fn WC_FOR_TYPE_only_uses_low_2_bits() {
6011        // 0b1011 → 0b11 = 3 (low 2 bits).
6012        let w = wc_bld(WC_FOR, 0b1011);
6013        assert_eq!(WC_FOR_TYPE(w), 3);
6014    }
6015
6016    // ═══════════════════════════════════════════════════════════════════
6017    // Additional C-parity tests for Src/zsh.h PM_* parameter flag bits
6018    // c:3174-3206 — scalar/array/integer/float/hashed/case/readonly/etc.
6019    // ═══════════════════════════════════════════════════════════════════
6020
6021    /// c:3174 — `PM_SCALAR` = 0 (default param type).
6022    #[test]
6023    fn pm_scalar_is_zero() {
6024        assert_eq!(PM_SCALAR, 0, "PM_SCALAR is the default (no bits)");
6025    }
6026
6027    /// c:3176-3184 — PM_ARRAY/INTEGER/EFLOAT/FFLOAT/HASHED canonical bit positions.
6028    #[test]
6029    fn pm_type_flags_canonical_bit_positions() {
6030        assert_eq!(PM_ARRAY, 1 << 0, "c:3176");
6031        assert_eq!(PM_INTEGER, 1 << 1, "c:3178");
6032        assert_eq!(PM_EFLOAT, 1 << 2, "c:3180");
6033        assert_eq!(PM_FFLOAT, 1 << 3, "c:3182");
6034        assert_eq!(PM_HASHED, 1 << 4, "c:3184");
6035    }
6036
6037    /// c:3186-3194 — PM_LEFT/RIGHT_B/RIGHT_Z/LOWER/UPPER canonical bits.
6038    #[test]
6039    fn pm_padding_case_flags_canonical_bit_positions() {
6040        assert_eq!(PM_LEFT, 1 << 5, "c:3186");
6041        assert_eq!(PM_RIGHT_B, 1 << 6, "c:3188");
6042        assert_eq!(PM_RIGHT_Z, 1 << 7, "c:3190");
6043        assert_eq!(PM_LOWER, 1 << 8, "c:3192");
6044        assert_eq!(PM_UPPER, 1 << 9, "c:3194");
6045    }
6046
6047    /// c:3196 — PM_UNDEFINED INTENTIONALLY aliases PM_UPPER (both 1<<9).
6048    /// Per c:3196 comment, autoload-undefined fns reuse the UPPER bit
6049    /// because undefined fns can never have case attributes.
6050    #[test]
6051    fn pm_undefined_aliases_pm_upper() {
6052        assert_eq!(
6053            PM_UNDEFINED, PM_UPPER,
6054            "c:3196 INTENTIONAL alias: undefined fns reuse UPPER bit"
6055        );
6056    }
6057
6058    /// c:3204 — PM_ABSPATH_USED INTENTIONALLY aliases PM_EXPORTED.
6059    /// c:3204 comment: only-for-internal-tracking flag reuses exported bit.
6060    #[test]
6061    fn pm_abspath_used_aliases_pm_exported() {
6062        assert_eq!(
6063            PM_ABSPATH_USED, PM_EXPORTED,
6064            "c:3204 INTENTIONAL alias: bit reuse for path-tracking"
6065        );
6066    }
6067
6068    /// c:3198 — PM_READONLY = 1 << 10.
6069    #[test]
6070    fn pm_readonly_is_bit_10() {
6071        assert_eq!(PM_READONLY, 1 << 10);
6072    }
6073
6074    /// c:3174-3206 — PM_* flags are all u32 (compile-time type pin).
6075    #[test]
6076    fn pm_flags_are_u32_type() {
6077        let _: u32 = PM_SCALAR;
6078        let _: u32 = PM_ARRAY;
6079        let _: u32 = PM_INTEGER;
6080        let _: u32 = PM_READONLY;
6081    }
6082
6083    /// c:3176-3194 — distinct type flags are pairwise disjoint
6084    /// (excluding intentional aliases like UNDEFINED=UPPER).
6085    #[test]
6086    fn pm_distinct_type_flags_pairwise_disjoint() {
6087        let codes = [
6088            PM_ARRAY, PM_INTEGER, PM_EFLOAT, PM_FFLOAT, PM_HASHED, PM_LEFT, PM_RIGHT_B, PM_RIGHT_Z,
6089            PM_LOWER, PM_UPPER,
6090        ];
6091        let unique: std::collections::HashSet<_> = codes.iter().copied().collect();
6092        assert_eq!(
6093            unique.len(),
6094            codes.len(),
6095            "distinct PM type/padding flags must be pairwise disjoint"
6096        );
6097    }
6098
6099    /// c:3176-3194 — every distinct PM_* type/padding flag is a single bit.
6100    #[test]
6101    fn pm_distinct_type_flags_all_single_bits() {
6102        for &v in &[
6103            PM_ARRAY,
6104            PM_INTEGER,
6105            PM_EFLOAT,
6106            PM_FFLOAT,
6107            PM_HASHED,
6108            PM_LEFT,
6109            PM_RIGHT_B,
6110            PM_RIGHT_Z,
6111            PM_LOWER,
6112            PM_UPPER,
6113            PM_READONLY,
6114            PM_TAGGED,
6115            PM_EXPORTED,
6116            PM_UNIQUE,
6117        ] {
6118            assert!(
6119                v.is_power_of_two(),
6120                "PM_* flag {:#x} must be a single bit",
6121                v
6122            );
6123        }
6124    }
6125
6126    /// c:3174 — PM_SCALAR being 0 means "all flags clear" matches PM_SCALAR.
6127    #[test]
6128    fn pm_scalar_zero_is_default_state() {
6129        assert_eq!(
6130            PM_SCALAR & (PM_ARRAY | PM_INTEGER | PM_HASHED),
6131            0,
6132            "PM_SCALAR=0 by design: clear-all-bits state"
6133        );
6134    }
6135
6136    // ═══════════════════════════════════════════════════════════════════
6137    // Additional C-parity tests for Src/zsh.h PRINT_* + HIST_* flag bits
6138    // c:3400-3428 PRINT_* / c:3452-3464 HIST_*
6139    // ═══════════════════════════════════════════════════════════════════
6140
6141    /// c:3400-3418 — PRINT_NAMEONLY/TYPE/LIST/KV_PAIR/INCLUDEVALUE/TYPESET/
6142    /// LINE/POSIX_EXPORT/POSIX_READONLY/WITH_NAMESPACE canonical bits 0-9.
6143    #[test]
6144    fn print_canonical_bit_positions() {
6145        assert_eq!(PRINT_NAMEONLY, 1 << 0, "c:3400");
6146        assert_eq!(PRINT_TYPE, 1 << 1, "c:3402");
6147        assert_eq!(PRINT_LIST, 1 << 2, "c:3404");
6148        assert_eq!(PRINT_KV_PAIR, 1 << 3, "c:3406");
6149        assert_eq!(PRINT_INCLUDEVALUE, 1 << 4, "c:3408");
6150        assert_eq!(PRINT_TYPESET, 1 << 5, "c:3410");
6151        assert_eq!(PRINT_LINE, 1 << 6, "c:3412");
6152        assert_eq!(PRINT_POSIX_EXPORT, 1 << 7, "c:3414");
6153        assert_eq!(PRINT_POSIX_READONLY, 1 << 8, "c:3416");
6154        assert_eq!(PRINT_WITH_NAMESPACE, 1 << 9, "c:3418");
6155    }
6156
6157    /// c:3420 — PRINT_WHENCE_CSH INTENTIONALLY aliases PRINT_POSIX_EXPORT
6158    /// (both bit 7). Per c:2191 comment: typeset and whence are separate
6159    /// builtins, so flag-bit reuse is safe.
6160    #[test]
6161    fn print_whence_csh_aliases_print_posix_export() {
6162        assert_eq!(
6163            PRINT_WHENCE_CSH, PRINT_POSIX_EXPORT,
6164            "c:3420 INTENTIONAL alias: whence vs typeset disambiguated by builtin"
6165        );
6166    }
6167
6168    /// c:3422 — PRINT_WHENCE_VERBOSE aliases PRINT_POSIX_READONLY.
6169    #[test]
6170    fn print_whence_verbose_aliases_print_posix_readonly() {
6171        assert_eq!(
6172            PRINT_WHENCE_VERBOSE, PRINT_POSIX_READONLY,
6173            "c:3422 INTENTIONAL alias"
6174        );
6175    }
6176
6177    /// c:3424 — PRINT_WHENCE_SIMPLE aliases PRINT_WITH_NAMESPACE.
6178    #[test]
6179    fn print_whence_simple_aliases_print_with_namespace() {
6180        assert_eq!(
6181            PRINT_WHENCE_SIMPLE, PRINT_WITH_NAMESPACE,
6182            "c:3424 INTENTIONAL alias"
6183        );
6184    }
6185
6186    /// c:3400-3428 — every PRINT_* flag is i32 type (compile-time pin).
6187    #[test]
6188    fn print_flags_are_i32_type() {
6189        let _: i32 = PRINT_NAMEONLY;
6190        let _: i32 = PRINT_TYPE;
6191        let _: i32 = PRINT_WHENCE_CSH;
6192    }
6193
6194    /// c:3452-3464 — HIST_MAKEUNIQUE/OLD/READ/DUP/FOREIGN/TMPSTORE/NOWRITE
6195    /// canonical bit values match upstream zsh-source positions.
6196    #[test]
6197    fn hist_canonical_bit_positions() {
6198        assert_eq!(HIST_MAKEUNIQUE, 0x01, "c:3452");
6199        assert_eq!(HIST_OLD, 0x02, "c:3454");
6200        assert_eq!(HIST_READ, 0x04, "c:3456");
6201        assert_eq!(HIST_DUP, 0x08, "c:3458");
6202        assert_eq!(HIST_FOREIGN, 0x10, "c:3460");
6203        assert_eq!(HIST_TMPSTORE, 0x20, "c:3462");
6204        assert_eq!(HIST_NOWRITE, 0x40, "c:3464");
6205    }
6206
6207    /// c:3452-3464 — all HIST_* are u32 type (compile-time pin).
6208    #[test]
6209    fn hist_flags_are_u32_type() {
6210        let _: u32 = HIST_MAKEUNIQUE;
6211        let _: u32 = HIST_OLD;
6212        let _: u32 = HIST_NOWRITE;
6213    }
6214
6215    /// c:3452-3464 — HIST_* are all single bits.
6216    #[test]
6217    fn hist_flags_all_single_bits() {
6218        for &v in &[
6219            HIST_MAKEUNIQUE,
6220            HIST_OLD,
6221            HIST_READ,
6222            HIST_DUP,
6223            HIST_FOREIGN,
6224            HIST_TMPSTORE,
6225            HIST_NOWRITE,
6226        ] {
6227            assert!(v.is_power_of_two(), "HIST_* {:#x} must be a single bit", v);
6228        }
6229    }
6230
6231    /// c:3452-3464 — HIST_* are pairwise distinct.
6232    #[test]
6233    fn hist_flags_pairwise_distinct() {
6234        let codes = [
6235            HIST_MAKEUNIQUE,
6236            HIST_OLD,
6237            HIST_READ,
6238            HIST_DUP,
6239            HIST_FOREIGN,
6240            HIST_TMPSTORE,
6241            HIST_NOWRITE,
6242        ];
6243        let unique: std::collections::HashSet<_> = codes.iter().copied().collect();
6244        assert_eq!(
6245            unique.len(),
6246            codes.len(),
6247            "HIST_* must be pairwise distinct"
6248        );
6249    }
6250
6251    /// c:3400-3418 — first 7 non-aliased PRINT_* bits are pairwise distinct.
6252    #[test]
6253    fn print_non_aliased_pairwise_distinct() {
6254        let codes = [
6255            PRINT_NAMEONLY,
6256            PRINT_TYPE,
6257            PRINT_LIST,
6258            PRINT_KV_PAIR,
6259            PRINT_INCLUDEVALUE,
6260            PRINT_TYPESET,
6261            PRINT_LINE,
6262        ];
6263        let unique: std::collections::HashSet<_> = codes.iter().copied().collect();
6264        assert_eq!(
6265            unique.len(),
6266            codes.len(),
6267            "non-aliased PRINT_* must be pairwise distinct"
6268        );
6269    }
6270}