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