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;
24use std::sync::atomic::Ordering;
25
26// =============================================================================
27// 1. Integer type aliases (zsh.h:30-92).
28// =============================================================================
29
30/// Port of `#define minimum(a,b)` from `Src/zsh.h:31`.
31#[inline]
32#[allow(non_snake_case)]
33pub fn minimum<T: PartialOrd>(a: T, b: T) -> T {
34    // c:31
35    if a < b {
36        a
37    } else {
38        b
39    }
40}
41
42/// Port of `typedef ZSH_64_BIT_TYPE zlong;` from `Src/zsh.h:38`.
43/// On every modern platform this is `int64_t` / `i64`.
44#[allow(non_camel_case_types)]
45pub type zlong = i64; // c:38
46
47/// Port of `typedef ZSH_64_BIT_UTYPE zulong;` from `Src/zsh.h:50`.
48#[allow(non_camel_case_types)]
49pub type zulong = u64; // c:50
50
51/// Port of `#define ZLONG_MAX` from `Src/zsh.h:40-57`.
52pub const ZLONG_MAX: zlong = i64::MAX; // c:40-57
53
54// =============================================================================
55// 2. mnumber + math-fn types (zsh.h:94-136).
56// =============================================================================
57
58/// Port of `mnumber` from `Src/zsh.h:95-101`. C definition:
59///
60/// ```c
61/// typedef struct {
62///     union { zlong l; double d; } u;
63///     int type;
64/// } mnumber;
65/// ```
66///
67/// The C union is represented here with both alternatives held as
68/// sibling fields plus a discriminant — `type_` selects which side
69/// of the prior `u` union is live. Read `l` when
70/// `type_ == MN_INTEGER`, read `d` when `type_ == MN_FLOAT`.
71#[allow(non_camel_case_types)]
72#[derive(Debug, Clone, Copy)] // c:95
73pub struct mnumber {
74    // c:95
75    pub l: i64,     // c:97 (u.l)
76    pub d: f64,     // c:98 (u.d)
77    pub type_: u32, // c:100 (type)
78}
79/// `MN_INTEGER` from `Src/zsh.h:103`.
80pub const MN_INTEGER: u32 = 1; // c:103
81/// `MN_FLOAT` from `Src/zsh.h:104`.
82pub const MN_FLOAT: u32 = 2; // c:104
83/// `MN_UNSET` from `Src/zsh.h:105` — `mnumber not yet retrieved`.
84pub const MN_UNSET: u32 = 4; // c:105
85
86/// Port of `typedef struct mathfunc *MathFunc;` from `Src/zsh.h:107`.
87pub type MathFunc = Box<mathfunc>; // c:107
88
89/// Port of `typedef mnumber (*NumMathFunc)(...)` from `Src/zsh.h:108`.
90pub type NumMathFunc = fn(name: &str, argc: i32, argv: &[mnumber], id: i32) -> mnumber;
91
92/// Port of `typedef mnumber (*StrMathFunc)(...)` from `Src/zsh.h:109`.
93pub type StrMathFunc = fn(name: &str, arg: &str, id: i32) -> mnumber;
94
95/// Port of `struct mathfunc` from `Src/zsh.h:111-121`.
96#[allow(non_camel_case_types)]
97pub struct mathfunc {
98    // c:111
99    pub next: Option<Box<mathfunc>>, // c:112
100    pub name: String,                // c:113
101    pub flags: i32,                  // c:114
102    pub nfunc: Option<NumMathFunc>,  // c:115
103    pub sfunc: Option<StrMathFunc>,  // c:116
104    pub module: Option<String>,      // c:117
105    pub minargs: i32,                // c:118
106    pub maxargs: i32,                // c:119
107    pub funcid: i32,                 // c:120
108}
109
110pub const MFF_STR: i32 = 1; // c:124
111pub const MFF_ADDED: i32 = 2; // c:126
112pub const MFF_USERFUNC: i32 = 4; // c:128
113pub const MFF_AUTOALL: i32 = 8; // c:130
114
115// =============================================================================
116// 3. Meta byte + parser tokens (zsh.h:144-224).
117// =============================================================================
118
119pub const META: char = '\u{83}'; // c:144
120pub const DEFAULT_IFS: &str = " \t\n\u{83} "; // c:149
121pub const DEFAULT_IFS_SH: &str = " \t\n"; // c:153
122
123// `DEFAULT_FCEDIT` / `DEFAULT_HISTSIZE` belong to `config.h`, not
124// `zsh.h` — they live in `src/ported/config_h.rs` (per PORT.md
125// Rule C). Callers use `crate::ported::config_h::DEFAULT_FCEDIT` /
126// `crate::ported::config_h::DEFAULT_HISTSIZE` (cast `as i64` where
127// the destination is `histsiz: AtomicI64`).
128
129// Byte-token constants — names match C `Src/zsh.h:159-224` exactly
130// (PascalCase). One exception: C `String` (`0x85`) would shadow Rust's
131// `std::string::String`, so we use `Stringg` for that single token.
132#[allow(non_upper_case_globals)] pub const Pound: char      = '\u{84}'; // c:159 #
133pub const Stringg: char = '\u{85}'; // c:160 $ — C `String` (renamed: collides with std::string::String)
134#[allow(non_upper_case_globals)] pub const Hat: char        = '\u{86}'; // c:161 ^
135#[allow(non_upper_case_globals)] pub const Star: char       = '\u{87}'; // c:162 *
136#[allow(non_upper_case_globals)] pub const Inpar: char      = '\u{88}'; // c:163 (
137#[allow(non_upper_case_globals)] pub const Inparmath: char  = '\u{89}'; // c:164 ((
138#[allow(non_upper_case_globals)] pub const Outpar: char     = '\u{8a}'; // c:165 )
139#[allow(non_upper_case_globals)] pub const Outparmath: char = '\u{8b}'; // c:166 ))
140#[allow(non_upper_case_globals)] pub const Qstring: char    = '\u{8c}'; // c:167 "$"
141#[allow(non_upper_case_globals)] pub const Equals: char     = '\u{8d}'; // c:168 =
142#[allow(non_upper_case_globals)] pub const Bar: char        = '\u{8e}'; // c:169 |
143#[allow(non_upper_case_globals)] pub const Inbrace: char    = '\u{8f}'; // c:170 {
144#[allow(non_upper_case_globals)] pub const Outbrace: char   = '\u{90}'; // c:171 }
145#[allow(non_upper_case_globals)] pub const Inbrack: char    = '\u{91}'; // c:172 [
146#[allow(non_upper_case_globals)] pub const Outbrack: char   = '\u{92}'; // c:173 ]
147#[allow(non_upper_case_globals)] pub const Tick: char       = '\u{93}'; // c:174 `
148#[allow(non_upper_case_globals)] pub const Inang: char      = '\u{94}'; // c:175 <
149#[allow(non_upper_case_globals)] pub const Outang: char     = '\u{95}'; // c:176 >
150#[allow(non_upper_case_globals)] pub const OutangProc: char = '\u{96}'; // c:177 >(...)
151#[allow(non_upper_case_globals)] pub const Quest: char      = '\u{97}'; // c:178 ?
152#[allow(non_upper_case_globals)] pub const Tilde: char      = '\u{98}'; // c:179 ~
153#[allow(non_upper_case_globals)] pub const Qtick: char      = '\u{99}'; // c:180 "`"
154#[allow(non_upper_case_globals)] pub const Comma: char      = '\u{9a}'; // c:181 ,
155#[allow(non_upper_case_globals)] pub const Dash: char       = '\u{9b}'; // c:182 -
156#[allow(non_upper_case_globals)] pub const Bang: char       = '\u{9c}'; // c:183 !
157pub const LAST_NORMAL_TOK: char = Bang; // c:188
158
159#[allow(non_upper_case_globals)] pub const Snull: char     = '\u{9d}'; // c:193
160#[allow(non_upper_case_globals)] pub const Dnull: char     = '\u{9e}'; // c:194
161#[allow(non_upper_case_globals)] pub const Bnull: char     = '\u{9f}'; // c:195
162#[allow(non_upper_case_globals)] pub const Bnullkeep: char = '\u{a0}'; // c:200
163#[allow(non_upper_case_globals)] pub const Nularg: char    = '\u{a1}'; // c:206
164#[allow(non_upper_case_globals)] pub const Marker: char    = '\u{a2}'; // c:224
165
166pub const SPECCHARS: &str = "#$^*()=|{}[]`<>?~;&\n\t \\\'\""; // c:228
167pub const PATCHARS: &str = "#^*()|[]<>?~\\"; // c:232
168
169/// Port of `#define IS_DASH(x)` from `Src/zsh.h:242`.
170#[inline]
171#[allow(non_snake_case)]
172pub fn IS_DASH(x: char) -> bool {
173    x == '-' || x == Dash
174} // c:242
175
176// =============================================================================
177// 4. Quote types (zsh.h:252-294).
178// =============================================================================
179
180pub const QT_NONE: i32 = 0; // c:257
181pub const QT_BACKSLASH: i32 = 1; // c:259
182pub const QT_SINGLE: i32 = 2; // c:261
183pub const QT_DOUBLE: i32 = 3; // c:263
184pub const QT_DOLLARS: i32 = 4; // c:265
185pub const QT_BACKTICK: i32 = 5; // c:271
186pub const QT_SINGLE_OPTIONAL: i32 = 6; // c:276
187pub const QT_BACKSLASH_PATTERN: i32 = 7; // c:282
188pub const QT_BACKSLASH_SHOWNULL: i32 = 8; // c:286
189pub const QT_QUOTEDZPUTS: i32 = 9; // c:291
190
191/// Port of `#define QT_IS_SINGLE(x)` from `Src/zsh.h:294`.
192#[inline]
193#[allow(non_snake_case)]
194pub fn QT_IS_SINGLE(x: i32) -> bool {
195    x == QT_SINGLE || x == QT_SINGLE_OPTIONAL
196}
197
198// =============================================================================
199// 5. Lexical tokens (zsh.h:304-371).
200// =============================================================================
201
202#[allow(non_camel_case_types)]
203pub type lextok = i32;
204pub const NULLTOK: lextok = 0; // c:305
205pub const SEPER: lextok = 1;
206pub const NEWLIN: lextok = 2;
207pub const SEMI: lextok = 3;
208pub const DSEMI: lextok = 4;
209pub const AMPER: lextok = 5;
210pub const INPAR_TOK: lextok = 6; // collision with char INPAR; suffix
211pub const OUTPAR_TOK: lextok = 7;
212pub const DBAR: lextok = 8;
213pub const DAMPER: lextok = 9;
214pub const OUTANG_TOK: lextok = 10; // collision with char OUTANG
215pub const OUTANGBANG: lextok = 11;
216pub const DOUTANG: lextok = 12;
217pub const DOUTANGBANG: lextok = 13;
218pub const INANG_TOK: lextok = 14;
219pub const INOUTANG: lextok = 15;
220pub const DINANG: lextok = 16;
221pub const DINANGDASH: lextok = 17;
222pub const INANGAMP: lextok = 18;
223pub const OUTANGAMP: lextok = 19;
224pub const AMPOUTANG: lextok = 20;
225pub const OUTANGAMPBANG: lextok = 21;
226pub const DOUTANGAMP: lextok = 22;
227pub const DOUTANGAMPBANG: lextok = 23;
228pub const TRINANG: lextok = 24;
229pub const BAR_TOK: lextok = 25;
230pub const BARAMP: lextok = 26;
231pub const INOUTPAR: lextok = 27;
232pub const DINPAR: lextok = 28;
233pub const DOUTPAR: lextok = 29;
234pub const AMPERBANG: lextok = 30;
235pub const SEMIAMP: lextok = 31;
236pub const SEMIBAR: lextok = 32;
237pub const DOUTBRACK: lextok = 33;
238pub const STRING_LEX: lextok = 34;
239pub const ENVSTRING: lextok = 35;
240pub const ENVARRAY: lextok = 36;
241pub const ENDINPUT: lextok = 37;
242pub const LEXERR: lextok = 38;
243pub const BANG_TOK: lextok = 39; // c:346
244pub const DINBRACK: lextok = 40;
245pub const INBRACE_TOK: lextok = 41;
246pub const OUTBRACE_TOK: lextok = 42;
247pub const CASE: lextok = 43;
248pub const COPROC: lextok = 44;
249pub const DOLOOP: lextok = 45;
250pub const DONE: lextok = 46;
251pub const ELIF: lextok = 47;
252pub const ELSE: lextok = 48;
253pub const ZEND: lextok = 49;
254pub const ESAC: lextok = 50;
255pub const FI: lextok = 51;
256pub const FOR: lextok = 52;
257pub const FOREACH: lextok = 53;
258pub const FUNC: lextok = 54;
259pub const IF: lextok = 55;
260pub const NOCORRECT: lextok = 56;
261pub const REPEAT: lextok = 57;
262pub const SELECT: lextok = 58;
263pub const THEN: lextok = 59;
264pub const TIME: lextok = 60;
265pub const UNTIL: lextok = 61;
266pub const WHILE: lextok = 62;
267pub const TYPESET: lextok = 63; // c:370
268
269// =============================================================================
270// 6. Redirection types (zsh.h:377-408).
271// =============================================================================
272
273pub const REDIR_WRITE: i32 = 0;
274pub const REDIR_WRITENOW: i32 = 1;
275pub const REDIR_APP: i32 = 2;
276pub const REDIR_APPNOW: i32 = 3;
277pub const REDIR_ERRWRITE: i32 = 4;
278pub const REDIR_ERRWRITENOW: i32 = 5;
279pub const REDIR_ERRAPP: i32 = 6;
280pub const REDIR_ERRAPPNOW: i32 = 7;
281pub const REDIR_READWRITE: i32 = 8;
282pub const REDIR_READ: i32 = 9;
283pub const REDIR_HEREDOC: i32 = 10;
284pub const REDIR_HEREDOCDASH: i32 = 11;
285pub const REDIR_HERESTR: i32 = 12;
286pub const REDIR_MERGEIN: i32 = 13;
287pub const REDIR_MERGEOUT: i32 = 14;
288pub const REDIR_CLOSE: i32 = 15;
289pub const REDIR_INPIPE: i32 = 16;
290pub const REDIR_OUTPIPE: i32 = 17;
291
292pub const REDIR_TYPE_MASK: i32 = 0x1f; // c:397
293pub const REDIR_VARID_MASK: i32 = 0x20; // c:399
294pub const REDIR_FROM_HEREDOC_MASK: i32 = 0x40; // c:401
295
296#[inline]
297#[allow(non_snake_case)]
298pub fn IS_WRITE_FILE(x: i32) -> bool {
299    x >= REDIR_WRITE && x <= REDIR_READWRITE
300}
301#[inline]
302#[allow(non_snake_case)]
303pub fn IS_APPEND_REDIR(x: i32) -> bool {
304    IS_WRITE_FILE(x) && (x & 2) != 0
305}
306#[inline]
307#[allow(non_snake_case)]
308pub fn IS_CLOBBER_REDIR(x: i32) -> bool {
309    IS_WRITE_FILE(x) && (x & 1) != 0
310}
311#[inline]
312#[allow(non_snake_case)]
313pub fn IS_ERROR_REDIR(x: i32) -> bool {
314    x >= REDIR_ERRWRITE && x <= REDIR_ERRAPPNOW
315}
316#[inline]
317#[allow(non_snake_case)]
318pub fn IS_READFD(x: i32) -> bool {
319    (x >= REDIR_READWRITE && x <= REDIR_MERGEIN) || x == REDIR_INPIPE
320}
321#[inline]
322#[allow(non_snake_case)]
323pub fn IS_REDIROP(x: lextok) -> bool {
324    x >= OUTANG_TOK && x <= TRINANG
325}
326
327// =============================================================================
328// 7. fdtable values (zsh.h:415-465).
329// =============================================================================
330
331pub const FDT_UNUSED: i32 = 0; // c:416
332pub const FDT_INTERNAL: i32 = 1; // c:421
333pub const FDT_EXTERNAL: i32 = 2; // c:426
334pub const FDT_MODULE: i32 = 3; // c:433
335pub const FDT_XTRACE: i32 = 4; // c:437
336pub const FDT_FLOCK: i32 = 5; // c:441
337pub const FDT_FLOCK_EXEC: i32 = 6; // c:446
338pub const FDT_PROC_SUBST: i32 = 7; // c:454
339pub const FDT_TYPE_MASK: i32 = 15; // c:458
340pub const FDT_SAVED_MASK: i32 = 16; // c:465
341
342// =============================================================================
343// 8. Input-stack flags (zsh.h:468-476).
344// =============================================================================
345
346pub const INP_FREE: i32 = 1 << 0; // c:468
347pub const INP_ALIAS: i32 = 1 << 1; // c:469
348pub const INP_HIST: i32 = 1 << 2; // c:470
349pub const INP_CONT: i32 = 1 << 3; // c:471
350pub const INP_ALCONT: i32 = 1 << 4; // c:472
351pub const INP_HISTCONT: i32 = 1 << 5; // c:473
352pub const INP_LINENO: i32 = 1 << 6; // c:474
353pub const INP_APPEND: i32 = 1 << 7; // c:475
354pub const INP_RAW_KEEP: i32 = 1 << 8; // c:476
355
356// =============================================================================
357// 9. metafy flags (zsh.h:479-486).
358// =============================================================================
359
360pub const META_REALLOC: i32 = 0; // c:479
361pub const META_USEHEAP: i32 = 1;
362pub const META_STATIC: i32 = 2;
363pub const META_DUP: i32 = 3;
364pub const META_ALLOC: i32 = 4;
365pub const META_NOALLOC: i32 = 5;
366pub const META_HEAPDUP: i32 = 6;
367pub const META_HREALLOC: i32 = 7;
368
369// =============================================================================
370// 10. ZCONTEXT_* (zsh.h:489-496) + entersubsh_ret (c:499-504).
371// =============================================================================
372
373pub const ZCONTEXT_HIST: i32 = 1 << 0; // c:491
374pub const ZCONTEXT_LEX: i32 = 1 << 1; // c:493
375pub const ZCONTEXT_PARSE: i32 = 1 << 2; // c:495
376
377#[derive(Default)]
378#[allow(non_camel_case_types)]
379pub struct entersubsh_ret {
380    // c:499
381    pub gleader: i32,       // c:501
382    pub list_pipe_job: i32, // c:503
383}
384
385// =============================================================================
386// 11. Linknode/linklist (zsh.h:557-572) + opaque pointer typedefs (c:510-549).
387// =============================================================================
388
389#[allow(non_camel_case_types)]
390pub struct linknode {
391    // c:557
392    pub next: Option<Box<linknode>>,
393    pub prev: Option<Box<linknode>>,
394    pub dat: usize,
395}
396#[allow(non_camel_case_types)]
397pub struct linklist {
398    // c:563
399    pub first: Option<Box<linknode>>,
400    pub last: Option<Box<linknode>>,
401    pub flags: i32,
402}
403pub type LinkNode = Box<linknode>; // c:533
404pub type LinkList = Box<linklist>; // c:534
405
406// Pointer typedefs for the ~50 struct types declared at c:510-549.
407// Each maps to a Box of the matching struct, with full field-by-field
408// body ports below (organized by C source line). Forward typedefs
409// here so structs that reference each other (e.g. param.old: Param)
410// can compile.
411
412pub type Alias = Box<alias>; // c:510
413pub type Asgment = Box<asgment>; // c:511
414pub type Builtin = Box<builtin>; // c:512
415pub type Cmdnam = Box<cmdnam>; // c:513
416// `struct complist` body lives in `crate::ported::glob` (mirrors
417// C: declared in zsh.h via typedef alias, body defined in glob.c).
418// The `Complist` alias here resolves to that struct.
419pub type Complist = Box<crate::ported::glob::complist>;                      // c:514
420pub type Conddef = Box<conddef>; // c:515
421pub type Dirsav = Box<dirsav>; // c:516
422pub type Emulation_options = Box<emulation_options>; // c:517
423pub type Execcmd_params = Box<execcmd_params>; // c:518
424pub type Features = Box<features>; // c:519
425pub type Feature_enables = Box<feature_enables>; // c:520
426pub type Funcstack = Box<funcstack>; // c:521
427pub type FuncWrap = Box<funcwrap>; // c:522
428pub type HashNode = Box<hashnode>; // c:523
429pub type HashTable = Box<hashtable>; // c:524
430pub type Heap = Box<heap>; // c:525
431pub type Heapstack = Box<heapstack>; // c:526
432pub type Histent = Box<histent>; // c:527
433pub type Hookdef = Box<hookdef>; // c:528
434pub type Imatchdata = Box<imatchdata>; // c:529
435pub type Job = Box<job>; // c:531
436pub type Jobfile = Box<jobfile>; // c:530
437pub type Linkedmod = Box<linkedmod>; // c:532
438pub type Module = Box<module>; // c:535
439pub type Nameddir = Box<nameddir>; // c:536
440pub type Options = Box<options>; // c:537
441pub type Optname = Box<optname>; // c:538
442pub type Param = Box<param>; // c:539
443pub type Paramdef = Box<paramdef>; // c:540
444pub type Patstralloc = Box<patstralloc>; // c:541
445pub type Patprog = Box<patprog>; // c:542
446pub type Prepromptfn = Box<prepromptfn>; // c:543
447pub type Process = Box<process>; // c:544
448pub type Redir = Box<redir>; // c:545
449pub type Reswd = Box<reswd>; // c:546
450pub type Shfunc = Box<shfunc>; // c:547
451pub type Timedfn = Box<timedfn>; // c:548
452pub type Value = Box<value>; // c:549
453
454pub type voidvoidfnptr_t = fn(); // c:621
455
456// Body-by-body struct definitions (C source order, fields verbatim
457// from zsh.h). Reserved-keyword Rust fields renamed minimally
458// (type→typ, str→str, match→match_, new→new_, loop→loop_,
459// mod→mod_, fn→fn_, ref→ref_, in→in_, where→where_).
460
461/// Port of `struct prepromptfn` from `Src/zsh.h:626-628`.
462#[allow(non_camel_case_types)]
463pub struct prepromptfn {
464    // c:626
465    pub func: voidvoidfnptr_t,
466}
467
468/// Port of `struct timedfn` from `Src/zsh.h:634-637`.
469#[allow(non_camel_case_types)]
470pub struct timedfn {
471    // c:634
472    pub func: voidvoidfnptr_t,
473    pub when: i64, // time_t
474}
475
476// `enum CaseMod` deleted — C uses an anonymous enum at
477// `Src/zsh.h:3122-3127` with bare values 0..3. Canonical `CASMOD_*`
478// `pub const`s are below at the "42. Case modify" section, matching
479// C's structural placement.
480
481/// Port of `typedef int (*CondHandler)(...)` from `Src/zsh.h:681`.
482pub type CondHandler = fn(args: &[String], id: i32) -> i32;
483
484/// Port of `struct conddef` from `Src/zsh.h:683-692`.
485#[allow(non_camel_case_types)]
486pub struct conddef {
487    // c:683
488    pub next: Option<Conddef>,        // c:684
489    pub name: String,                 // c:685
490    pub flags: i32,                   // c:686 CONDF_*
491    pub handler: Option<CondHandler>, // c:687
492    pub min: i32,                     // c:688
493    pub max: i32,                     // c:689
494    pub condid: i32,                  // c:690
495    pub module: Option<String>,       // c:691
496}
497
498/// Port of `struct dirsav` from `Src/zsh.h:1159-1164`.
499#[allow(non_camel_case_types)]
500pub struct dirsav {
501    // c:1159
502    pub dirfd: i32,              // c:1160
503    pub level: i32,              // c:1160
504    pub dirname: Option<String>, // c:1161
505    pub dev: u64,                // c:1162 dev_t
506    pub ino: u64,                // c:1163 ino_t
507}
508
509/// Port of `struct hashnode` from `Src/zsh.h:1226-1230`.
510#[allow(non_camel_case_types)]
511#[derive(Debug, Clone)]
512pub struct hashnode {
513    // c:1226
514    pub next: Option<HashNode>, // c:1227
515    pub nam: String,            // c:1228
516    pub flags: i32,             // c:1229
517}
518
519// hashtable function-pointer typedefs (zsh.h:1175-1193).
520pub type VFunc = fn(usize) -> usize; // c:1172
521pub type FreeFunc = fn(usize); // c:1173
522pub type HashFunc = fn(name: &str) -> u32; // c:1175
523pub type TableFunc = fn(table: &mut hashtable); // c:1176
524pub type AddNodeFunc = fn(table: &mut hashtable, name: String, val: usize);
525pub type GetNodeFunc = fn(table: &hashtable, name: &str) -> Option<HashNode>;
526pub type RemoveNodeFunc = fn(table: &mut hashtable, name: &str) -> Option<HashNode>;
527pub type FreeNodeFunc = fn(node: HashNode);
528pub type CompareFunc = fn(a: &str, b: &str) -> i32;
529pub type ScanFunc = fn(node: &HashNode, flags: i32);
530pub type ScanTabFunc = fn(table: &hashtable, func: ScanFunc, flags: i32);
531pub type PrintTableStats = fn(table: &hashtable);
532
533/// Port of `struct hashtable` from `Src/zsh.h:1200-1222`.
534#[allow(non_camel_case_types)]
535#[derive(Clone)]
536pub struct hashtable {
537    // c:1200
538    pub hsize: i32,                         // c:1202
539    pub ct: i32,                            // c:1203
540    pub nodes: Vec<Option<HashNode>>,       // c:1204
541    pub tmpdata: usize,                     // c:1205
542    pub hash: Option<HashFunc>,             // c:1208
543    pub emptytable: Option<TableFunc>,      // c:1209
544    pub filltable: Option<TableFunc>,       // c:1210
545    pub cmpnodes: Option<CompareFunc>,      // c:1211
546    pub addnode: Option<AddNodeFunc>,       // c:1212
547    pub getnode: Option<GetNodeFunc>,       // c:1213
548    pub getnode2: Option<GetNodeFunc>,      // c:1214
549    pub removenode: Option<RemoveNodeFunc>, // c:1216
550    pub disablenode: Option<ScanFunc>,      // c:1217
551    pub enablenode: Option<ScanFunc>,       // c:1218
552    pub freenode: Option<FreeNodeFunc>,     // c:1219
553    pub printnode: Option<ScanFunc>,        // c:1220
554    pub scantab: Option<ScanTabFunc>,       // c:1221
555}
556
557/// Port of `struct optname` from `Src/zsh.h:1239-1242`.
558#[allow(non_camel_case_types)]
559pub struct optname {
560    // c:1239
561    pub node: hashnode, // c:1240
562    pub optno: i32,     // c:1241
563}
564
565/// Port of `struct reswd` from `Src/zsh.h:1246-1249`.
566#[allow(non_camel_case_types)]
567#[derive(Debug, Clone)]
568pub struct reswd {
569    // c:1246
570    pub node: hashnode, // c:1247
571    pub token: i32,     // c:1248
572}
573
574/// Port of `struct alias` from `Src/zsh.h:1253-1257`.
575#[allow(non_camel_case_types)]
576#[derive(Debug, Clone)]
577pub struct alias {
578    // c:1253
579    pub node: hashnode, // c:1254
580    pub text: String,   // c:1255
581    pub inuse: i32,     // c:1256
582}
583
584/// Port of `struct asgment` from `Src/zsh.h:1267-1275`. Note the C
585/// union is split into two Option<…> fields here; only one is set
586/// per asgment (dispatched by `flags & ASG_ARRAY`).
587///
588/// `array` is typed `LinkList<String>` (the generic port in
589/// `src/ported/linklist.rs`) rather than the bare `LinkList` type
590/// alias above — C stores `char *` payload in the list, so the
591/// typed form is what `firstnode`/`getdata`/`nextnode` traversal
592/// expects at every assign-printing site (e.g. `Src/builtin.c:476-482`).
593#[allow(non_camel_case_types)]
594pub struct asgment {
595    // c:1267
596    pub node: linknode,                                          // c:1268
597    pub name: String,                                            // c:1269
598    pub flags: i32,                                              // c:1270 ASG_*
599    pub scalar: Option<String>,                                  // c:1272 union value.scalar
600    pub array: Option<crate::ported::linklist::LinkList<String>>, // c:1273 union value.array (LinkList of char *)
601}
602
603/// Port of `struct cmdnam` from `Src/zsh.h:1301-1308`. The C union
604/// `{ char **name; char *cmd; }` becomes two Option fields; only
605/// one is set per cmdnam (dispatched by `flags & HASHED`).
606#[allow(non_camel_case_types)]
607#[derive(Debug, Clone)]
608pub struct cmdnam {
609    // c:1301
610    pub node: hashnode,            // c:1302
611    pub name: Option<Vec<String>>, // c:1304 union u.name
612    pub cmd: Option<String>,       // c:1305 union u.cmd
613}
614
615/// Port of `struct shfunc` from `Src/zsh.h:1316-1325`.
616#[allow(non_camel_case_types)]
617#[derive(Debug, Clone)]
618pub struct shfunc {
619    // c:1316
620    pub node: hashnode,                    // c:1317
621    pub filename: Option<String>,          // c:1318
622    pub lineno: i64,                       // c:1321 zlong
623    pub funcdef: Option<Eprog>,            // c:1322
624    pub redir: Option<Eprog>,              // c:1323
625    pub sticky: Option<Emulation_options>, // c:1324
626    /// **RUST-ONLY EXTENSION (no C counterpart).** Raw source text
627    /// for deferred-compile path: zshrs stores the function body
628    /// as-typed and parses on first invocation, vs C eagerly
629    /// compiling into `funcdef: Eprog` at definition time. When
630    /// the fusevm bytecode cache lands, this field gets retired in
631    /// favor of populating `funcdef` directly (matching C). Until
632    /// then, both fields can be set: `funcdef` for compiled
633    /// callers, `body` for the lazy-compile path.
634    pub body: Option<String>,
635}
636
637/// Port of `struct funcstack` from `Src/zsh.h:1348-1356`.
638#[allow(non_camel_case_types)]
639#[derive(Clone, Default)]
640pub struct funcstack {
641    // c:1348
642    pub prev: Option<Funcstack>,  // c:1349
643    pub name: String,             // c:1350
644    pub filename: Option<String>, // c:1351
645    pub caller: Option<String>,   // c:1352
646    pub flineno: i64,             // c:1353
647    pub lineno: i64,              // c:1354
648    pub tp: i32,                  // c:1355 FS_*
649}
650
651/// Port of `typedef int (*WrapFunc)(...)` from `Src/zsh.h:1360`.
652pub type WrapFunc = fn(prog: Eprog, w: FuncWrap, name: &str) -> i32;
653
654/// Port of `struct funcwrap` from `Src/zsh.h:1362-1367`.
655#[allow(non_camel_case_types)]
656pub struct funcwrap {
657    // c:1362
658    pub next: Option<FuncWrap>,    // c:1363
659    pub flags: i32,                // c:1364
660    pub handler: Option<WrapFunc>, // c:1365
661    pub module: Option<Module>,    // c:1366
662}
663
664/// Port of `struct builtin` from `Src/zsh.h:1440-1448`.
665#[allow(non_camel_case_types)]
666pub struct builtin {
667    // c:1440
668    pub node: hashnode,                   // c:1441
669    pub handlerfunc: Option<HandlerFunc>, // c:1442
670    pub minargs: i32,                     // c:1443
671    pub maxargs: i32,                     // c:1444
672    pub funcid: i32,                      // c:1445
673    pub optstr: Option<String>,           // c:1446
674    pub defopts: Option<String>,          // c:1447
675}
676
677/// Port of `struct execcmd_params` from `Src/zsh.h:1492-1501`.
678#[allow(non_camel_case_types)]
679pub struct execcmd_params {
680    // c:1492
681    pub args: Option<LinkList>,  // c:1493
682    pub redir: Option<LinkList>, // c:1494
683    pub beg: Wordcode,           // c:1495
684    pub varspc: Wordcode,        // c:1496
685    pub assignspc: Wordcode,     // c:1497
686    pub typ: i32,                // c:1498 (Rust keyword `type`)
687    pub postassigns: i32,        // c:1499
688    pub htok: i32,               // c:1500
689}
690
691/// Port of `struct module` from `Src/zsh.h:1503-1513`. C uses a union
692/// for handle/linked/alias dispatched implicitly by load type; Rust
693/// port keeps three Options.
694///
695/// `autoloads`/`deps` are `LinkList<String>` because C's untyped
696/// `LinkList` carries `char *` payload for these fields (see
697/// `Src/module.c:2392` `zaddlinknode(m->deps, dep)` etc.).
698#[allow(non_camel_case_types)]
699#[derive(Debug)]
700pub struct module {
701    // c:1503
702    pub node: hashnode,              // c:1504
703    pub handle: Option<usize>,       // c:1506 union.handle (void *)
704    pub linked: Option<Linkedmod>,   // c:1507 union.linked
705    pub alias: Option<String>,       // c:1508 union.alias
706    pub autoloads: Option<crate::ported::linklist::LinkList<String>>, // c:1510
707    pub deps: Option<crate::ported::linklist::LinkList<String>>,      // c:1511
708    pub wrapper: i32,                // c:1512
709}
710
711impl module {
712    /// Construct a fresh statically-linked module entry. Mirrors C's
713    /// `zshcalloc(sizeof(*m))` + `m->node.nam = ztrdup(name)` pattern
714    /// at `Src/module.c:361` (`register_module`).
715    pub fn new(name: &str) -> Self {
716        Self {
717            node: hashnode {
718                next: None,
719                nam: name.to_string(),
720                flags: MOD_LINKED,
721            },
722            handle: None,
723            linked: None,
724            alias: None,
725            autoloads: None,
726            deps: None,
727            wrapper: 0,
728        }
729    }
730
731    /// True if the module is currently usable.
732    /// Mirrors C's `MOD_BUSY`/`MOD_UNLOAD` checks at `Src/module.c:1703`
733    /// (`module_loaded`): the module exists and `MOD_UNLOAD` is clear.
734    pub fn is_loaded(&self) -> bool {
735        (self.node.flags & MOD_LINKED) != 0
736            && (self.node.flags & MOD_UNLOAD) == 0
737    }
738}
739
740/// Port of module fn-pointer typedefs from `Src/zsh.h:1534-1537`.
741pub type Module_generic_func = fn() -> i32;
742pub type Module_void_func = fn(m: &module) -> i32;
743pub type Module_features_func = fn(m: &module, features: &mut Vec<String>) -> i32;
744pub type Module_enables_func = fn(m: &module, enables: &mut Vec<i32>) -> i32;
745
746/// Port of `struct linkedmod` from `Src/zsh.h:1539-1547`.
747#[allow(non_camel_case_types)]
748#[derive(Debug)]
749pub struct linkedmod {
750    // c:1539
751    pub name: String,                           // c:1540
752    pub setup: Option<Module_void_func>,        // c:1541
753    pub features: Option<Module_features_func>, // c:1542
754    pub enables: Option<Module_enables_func>,   // c:1543
755    pub boot: Option<Module_void_func>,         // c:1544
756    pub cleanup: Option<Module_void_func>,      // c:1545
757    pub finish: Option<Module_void_func>,       // c:1546
758}
759
760/// Port of `struct features` from `Src/zsh.h:1553-1568`.
761#[allow(non_camel_case_types)]
762pub struct features {
763    // c:1553
764    pub bn_list: Option<Builtin>,  // c:1555
765    pub bn_size: i32,              // c:1556
766    pub cd_list: Option<Conddef>,  // c:1558
767    pub cd_size: i32,              // c:1559
768    pub mf_list: Option<MathFunc>, // c:1561
769    pub mf_size: i32,              // c:1562
770    pub pd_list: Option<Paramdef>, // c:1564
771    pub pd_size: i32,              // c:1565
772    pub n_abstract: i32,           // c:1567
773}
774
775/// Port of `struct feature_enables` from `Src/zsh.h:1573-1578`.
776#[allow(non_camel_case_types)]
777pub struct feature_enables {
778    // c:1573
779    pub str: String,         // c:1575 (Rust keyword `str`)
780    pub pat: Option<Patprog>, // c:1577
781}
782
783/// Port of `typedef int (*Hookfn)(...)` from `Src/zsh.h:1582`.
784pub type Hookfn = fn(def: Hookdef, data: usize) -> i32;
785
786/// Port of `struct hookdef` from `Src/zsh.h:1584-1590`.
787#[allow(non_camel_case_types)]
788pub struct hookdef {
789    // c:1584
790    pub next: Option<Hookdef>,   // c:1585
791    pub name: String,            // c:1586
792    pub def: Option<Hookfn>,     // c:1587
793    pub flags: i32,              // c:1588
794    pub funcs: Option<LinkList>, // c:1589
795}
796
797/// Port of `struct patprog` from `Src/zsh.h:1601-1611`.
798///
799/// C layout uses a trailing byte buffer accessed via
800/// `(char *)prog + prog->startoff`; the Rust port stores it inline
801/// as a `code: Vec<u8>` field. The opcode stream layout is preserved
802/// byte-for-byte — `startoff` and `size` index into `code`.
803///
804#[derive(Debug, Clone)]
805#[allow(non_camel_case_types)]
806pub struct patprog {
807    // c:1601
808    pub startoff: i64,  // c:1602
809    pub size: i64,      // c:1603
810    pub mustoff: i64,   // c:1604
811    pub patmlen: i64,   // c:1605
812    pub globflags: i32, // c:1606
813    pub globend: i32,   // c:1607
814    pub flags: i32,     // c:1608 PAT_*
815    pub patnpar: i32,   // c:1609
816    pub patstartch: u8, // c:1610 (last field per zsh.h)
817}
818
819/// Port of `struct patstralloc` from `Src/zsh.h:1613-1620`.
820#[allow(non_camel_case_types)]
821pub struct patstralloc {
822    // c:1613
823    pub unmetalen: i32,                // c:1614
824    pub unmetalenp: i32,               // c:1615
825    pub alloced: Option<String>,       // c:1617
826    pub progstrunmeta: Option<String>, // c:1618
827    pub progstrunmetalen: i32,         // c:1619
828}
829
830/// Port of `struct zpc_disables_save` from `Src/zsh.h:1681-1689`.
831#[allow(non_camel_case_types)]
832pub struct zpc_disables_save {
833    // c:1681
834    pub next: Option<Box<zpc_disables_save>>, // c:1682
835    pub disables: u32,                        // c:1688
836}
837pub type Zpc_disables_save = Box<zpc_disables_save>; // c:1691
838
839/// Port of `struct imatchdata` from `Src/zsh.h:1740-1760`.
840#[allow(non_camel_case_types)]
841pub struct imatchdata {
842    // c:1740
843    pub mstr: Option<String>,       // c:1742
844    pub mlen: i32,                  // c:1744
845    pub ustr: Option<String>,       // c:1746
846    pub ulen: i32,                  // c:1748
847    pub flags: i32,                 // c:1750 SUB_*
848    pub replstr: Option<String>,    // c:1752
849    pub repllist: Option<LinkList>, // c:1759
850}
851
852// gsu_* function-pointer typedefs (zsh.h:1790-1794) + structs.
853pub type GsuScalar = Box<gsu_scalar>; // c:1790
854pub type GsuInteger = Box<gsu_integer>; // c:1791
855pub type GsuFloat = Box<gsu_float>; // c:1792
856pub type GsuArray = Box<gsu_array>; // c:1793
857pub type GsuHash = Box<gsu_hash>; // c:1794
858
859#[allow(non_camel_case_types)]
860#[derive(Clone)]
861pub struct gsu_scalar {
862    // c:1796
863    pub getfn: fn(pm: &param) -> String,        // c:1797
864    pub setfn: fn(pm: &mut param, val: String), // c:1798
865    pub unsetfn: fn(pm: &mut param, exp: i32),  // c:1799
866}
867#[allow(non_camel_case_types)]
868#[derive(Clone)]
869pub struct gsu_integer {
870    // c:1802
871    pub getfn: fn(pm: &param) -> i64,
872    pub setfn: fn(pm: &mut param, val: i64),
873    pub unsetfn: fn(pm: &mut param, exp: i32),
874}
875#[allow(non_camel_case_types)]
876#[derive(Clone)]
877pub struct gsu_float {
878    // c:1808
879    pub getfn: fn(pm: &param) -> f64,
880    pub setfn: fn(pm: &mut param, val: f64),
881    pub unsetfn: fn(pm: &mut param, exp: i32),
882}
883#[allow(non_camel_case_types)]
884#[derive(Clone)]
885pub struct gsu_array {
886    // c:1814
887    pub getfn: fn(pm: &param) -> Vec<String>,
888    pub setfn: fn(pm: &mut param, val: Vec<String>),
889    pub unsetfn: fn(pm: &mut param, exp: i32),
890}
891#[allow(non_camel_case_types)]
892#[derive(Clone)]
893pub struct gsu_hash {
894    // c:1820
895    pub getfn: fn(pm: &param) -> Option<&HashTable>,
896    pub setfn: fn(pm: &mut param, val: HashTable),
897    pub unsetfn: fn(pm: &mut param, exp: i32),
898}
899
900/// Port of `struct param` from `Src/zsh.h:1829-1867`. The C unions
901/// `u` (data) and `gsu` (vtable) are flattened into per-variant
902/// fields; the dispatcher looks at `node.flags & PM_TYPE` and reads
903/// the matching field.
904#[allow(non_camel_case_types)]
905#[derive(Clone)]
906pub struct param {
907    // c:1829
908    pub node: hashnode, // c:1830
909    // u union (c:1833-1842):
910    pub u_data: usize,              // c:1834 void *data
911    pub u_arr: Option<Vec<String>>, // c:1835 char **arr
912    pub u_str: Option<String>,      // c:1836 char *str
913    pub u_val: i64,                 // c:1837 zlong val
914    pub u_dval: f64,                // c:1839 double dval
915    pub u_hash: Option<HashTable>,  // c:1841 HashTable hash
916    // gsu vtable union (c:1852-1858):
917    pub gsu_s: Option<GsuScalar>,  // c:1853
918    pub gsu_i: Option<GsuInteger>, // c:1854
919    pub gsu_f: Option<GsuFloat>,   // c:1855
920    pub gsu_a: Option<GsuArray>,   // c:1856
921    pub gsu_h: Option<GsuHash>,    // c:1857
922    pub base: i32,                 // c:1860
923    pub width: i32,                // c:1862
924    pub env: Option<String>,       // c:1863
925    pub ename: Option<String>,     // c:1864
926    pub old: Option<Param>,        // c:1865
927    pub level: i32,                // c:1866
928}
929
930/// Port of `struct tieddata` from `Src/zsh.h:1870-1873`.
931#[allow(non_camel_case_types)]
932pub struct tieddata {
933    // c:1870
934    pub arrptr: Option<Vec<String>>, // c:1871 char ***arrptr
935    pub joinchar: i32,               // c:1872
936}
937
938/// Port of `struct repldata` from `Src/zsh.h:2003-2006`.
939#[allow(non_camel_case_types)]
940pub struct repldata {
941    // c:2003
942    pub b: i32,                  // c:2004
943    pub e: i32,                  // c:2004
944    pub replstr: Option<String>, // c:2005
945}
946pub type Repldata = Box<repldata>; // c:2007
947
948/// Port of `struct paramdef` from `Src/zsh.h:2082-2090`.
949#[allow(non_camel_case_types)]
950pub struct paramdef {
951    // c:2082
952    pub name: String,                 // c:2083
953    pub flags: i32,                   // c:2084
954    pub var: usize,                   // c:2085 void *
955    pub gsu: usize,                   // c:2086 const void *
956    pub getnfn: Option<GetNodeFunc>,  // c:2087
957    pub scantfn: Option<ScanTabFunc>, // c:2088
958    pub pm: Option<Param>,            // c:2089
959}
960
961/// Port of `struct nameddir` from `Src/zsh.h:2149-2153`.
962#[allow(non_camel_case_types)]
963#[derive(Clone)]
964pub struct nameddir {
965    // c:2149
966    pub node: hashnode, // c:2150
967    pub dir: String,    // c:2151
968    pub diff: i32,      // c:2152
969}
970
971/// Port of `groupmap` from `Src/zsh.h:2161-2166`.
972#[allow(non_camel_case_types)]
973pub struct groupmap {
974    // c:2161
975    pub name: String, // c:2163
976    pub gid: u32,     // c:2165 gid_t
977}
978pub type Groupmap = Box<groupmap>; // c:2167
979
980/// Port of `groupset` from `Src/zsh.h:2170-2175`.
981#[allow(non_camel_case_types)]
982pub struct groupset {
983    // c:2170
984    pub array: Vec<groupmap>, // c:2172
985    pub num: i32,             // c:2174
986}
987pub type Groupset = Box<groupset>; // c:2176
988
989/// Port of `struct histent` from `Src/zsh.h:2234-2250`.
990#[allow(non_camel_case_types)]
991pub struct histent {
992    // c:2234
993    pub node: hashnode,           // c:2235
994    pub up: Option<Histent>,      // c:2237
995    pub down: Option<Histent>,    // c:2238
996    pub zle_text: Option<String>, // c:2239
997    pub stim: i64,                // c:2244 time_t
998    pub ftim: i64,                // c:2245
999    pub words: Vec<i16>,          // c:2246
1000    pub nwords: i32,              // c:2248
1001    pub histnum: i64,             // c:2249 zlong
1002}
1003
1004/// Port of `struct emulation_options` from `Src/zsh.h:2570-2585`.
1005#[allow(non_camel_case_types)]
1006#[derive(Debug, Clone)]
1007pub struct emulation_options {
1008    // c:2570
1009    pub emulation: i32,          // c:2572
1010    pub n_on_opts: i32,          // c:2574
1011    pub n_off_opts: i32,         // c:2576
1012    pub on_opts: Vec<OptIndex>,  // c:2582
1013    pub off_opts: Vec<OptIndex>, // c:2584
1014}
1015
1016/// Port of `struct ttyinfo` from `Src/zsh.h:2593-2609`. The C
1017/// definition `#ifdef`-selects between `termios` / `termio` / sgtty.
1018/// Rust port stores the raw libc `termios` (the path taken on every
1019/// modern host).
1020#[allow(non_camel_case_types)]
1021#[derive(Debug, Clone)]
1022pub struct ttyinfo {
1023    // c:2593
1024    #[cfg(unix)]
1025    pub tio: libc::termios, // c:2595
1026    #[cfg(unix)]
1027    pub winsize: libc::winsize, // c:2607
1028}
1029
1030/// Port of `struct heapstack` from `Src/zsh.h:2871-2877`.
1031#[allow(non_camel_case_types)]
1032pub struct heapstack {
1033    // c:2871
1034    pub next: Option<Heapstack>, // c:2872
1035    pub used: usize,             // c:2873
1036}
1037
1038/// Port of `struct heap` from `Src/zsh.h:2881-2898`.
1039#[allow(non_camel_case_types)]
1040pub struct heap {
1041    // c:2881
1042    pub next: Option<Heap>,    // c:2882
1043    pub size: usize,           // c:2883
1044    pub used: usize,           // c:2884
1045    pub sp: Option<Heapstack>, // c:2885
1046}
1047
1048/// Port of `struct sortelt` from `Src/zsh.h:3013-3028`.
1049#[allow(non_camel_case_types)]
1050pub struct sortelt {
1051    // c:3013
1052    pub orig: String, // c:3015
1053    pub cmp: String,  // c:3017
1054    pub origlen: i32, // c:3022
1055    pub len: i32,     // c:3027
1056}
1057pub type SortElt = Box<sortelt>; // c:3030
1058
1059/// Port of `struct hist_stack` from `Src/zsh.h:3037-3058`.
1060#[allow(non_camel_case_types)]
1061pub struct hist_stack {
1062    // c:3037
1063    pub histactive: i32,        // c:3038
1064    pub histdone: i32,          // c:3039
1065    pub stophist: i32,          // c:3040
1066    pub hlinesz: i32,           // c:3041
1067    pub defev: i64,             // c:3042 zlong
1068    pub hline: Option<String>,  // c:3043
1069    pub hptr: Option<String>,   // c:3044
1070    pub chwords: Vec<i16>,      // c:3045
1071    pub chwordlen: i32,         // c:3046
1072    pub chwordpos: i32,         // c:3047
1073    pub csp: i32,               // c:3056
1074    pub hist_keep_comment: i32, // c:3057
1075}
1076
1077/// Port of `struct lexbufstate` from `Src/zsh.h:3069-3079`.
1078#[allow(non_camel_case_types)]
1079#[derive(Debug, Clone, Default)]
1080pub struct lexbufstate {
1081    // c:3069
1082    pub ptr: Option<String>, // c:3074
1083    pub siz: i32,            // c:3076
1084    pub len: i32,            // c:3078
1085}
1086
1087/// Port of `struct lex_stack` from `Src/zsh.h:3082-3096`.
1088#[allow(non_camel_case_types)]
1089#[derive(Debug, Clone, Default)]
1090pub struct lex_stack {
1091    // c:3082
1092    pub dbparens: i32,              // c:3083
1093    pub isfirstln: i32,             // c:3084
1094    pub isfirstch: i32,             // c:3085
1095    pub lexflags: i32,              // c:3086
1096    pub tok: lextok,                // c:3087
1097    pub tokstr: Option<String>,     // c:3088
1098    pub zshlextext: Option<String>, // c:3089
1099    pub lexbuf: lexbufstate,        // c:3090
1100    pub lex_add_raw: i32,           // c:3091
1101    pub tokstr_raw: Option<String>, // c:3092
1102    pub lexbuf_raw: lexbufstate,    // c:3093
1103    pub lexstop: i32,               // c:3094
1104    pub toklineno: i64,             // c:3095
1105}
1106
1107/// Port of `struct parse_stack` from `Src/zsh.h:3099-3116`.
1108#[allow(non_camel_case_types)]
1109pub struct parse_stack {
1110    // c:3099
1111    pub hdocs: Option<Box<heredocs>>, // c:3100
1112    pub incmdpos: i32,                // c:3102
1113    pub aliasspaceflag: i32,          // c:3103
1114    pub incond: i32,                  // c:3104
1115    pub inredir: i32,                 // c:3105
1116    pub incasepat: i32,               // c:3106
1117    pub isnewlin: i32,                // c:3107
1118    pub infor: i32,                   // c:3108
1119    pub inrepeat_: i32,               // c:3109
1120    pub intypeset: i32,               // c:3110
1121    pub eclen: i32,                   // c:3112
1122    pub ecused: i32,                  // c:3112
1123    pub ecnpats: i32,                 // c:3112
1124    pub ecbuf: Wordcode,              // c:3113
1125    pub ecstrs: Option<Eccstr>,       // c:3114
1126    pub ecsoffs: i32,                 // c:3115
1127    pub ecssub: i32,                  // c:3115
1128    pub ecnfunc: i32,                 // c:3115
1129}
1130
1131/// Port of `struct heredocs` from `Src/zsh.h:1152-1157`. Used by
1132/// parse_stack above.
1133#[allow(non_camel_case_types)]
1134pub struct heredocs {
1135    // c:1152
1136    pub next: Option<Box<heredocs>>, // c:1153
1137    pub typ: i32,                    // c:1154 (Rust keyword `type`)
1138    pub pc: i32,                     // c:1155
1139    pub str: Option<String>,        // c:1156
1140}
1141
1142/// Port of `struct execstack` from `Src/zsh.h:1127-1150`.
1143#[allow(non_camel_case_types)]
1144pub struct execstack {
1145    // c:1127
1146    pub next: Option<Box<execstack>>,      // c:1128
1147    pub list_pipe_pid: i32,                // c:1130 pid_t
1148    pub nowait: i32,                       // c:1131
1149    pub pline_level: i32,                  // c:1132
1150    pub list_pipe_child: i32,              // c:1133
1151    pub list_pipe_job: i32,                // c:1134
1152    pub list_pipe_text: [u8; JOBTEXTSIZE], // c:1135
1153    pub lastval: i32,                      // c:1136
1154    pub noeval: i32,                       // c:1137
1155    pub badcshglob: i32,                   // c:1138
1156    pub cmdoutpid: i32,                    // c:1139
1157    pub cmdoutval: i32,                    // c:1140
1158    pub use_cmdoutval: i32,                // c:1141
1159    pub procsubstpid: i32,                 // c:1142
1160    pub trap_return: i32,                  // c:1143
1161    pub trap_state: i32,                   // c:1144
1162    pub trapisfunc: i32,                   // c:1145
1163    pub traplocallevel: i32,               // c:1146
1164    pub noerrs: i32,                       // c:1147
1165    pub this_noerrexit: i32,               // c:1148
1166    pub underscore: Option<String>,        // c:1149
1167}
1168
1169/// Port of `struct process` from `Src/zsh.h:1117-1125`.
1170///
1171/// Field-shape deviations from the C struct (documented for the
1172/// `zsh.h ↔ zsh_h.rs` audit):
1173/// - `text`: `String` instead of `char text[JOBTEXTSIZE]`. The
1174///   `JOBTEXTSIZE` cap in C is a buffer-overflow guard; Rust's owned
1175///   String removes the cap without losing the field's semantic.
1176/// - `bgtime` / `endtime`: `Option<std::time::Instant>` instead of
1177///   `struct timespec`. C uses timespec for monotonic-clock points;
1178///   Rust's `Instant` is the equivalent abstraction.
1179/// - `next` removed: C threads `struct process *next` for the
1180///   in-job singly-linked list; Rust port owns the list externally
1181///   via `job.procs: Vec<process>` so callers don't carry the chain
1182///   pointer per node.
1183#[allow(non_camel_case_types)]
1184#[derive(Debug, Clone)]
1185pub struct process {
1186    // c:1117
1187    pub pid: i32,                                // c:1119 pid_t
1188    pub text: String,                            // c:1120 char text[JOBTEXTSIZE]
1189    pub status: i32,                             // c:1121
1190    pub ti: crate::ported::zsh_h::timeinfo,      // c:1122 child_times_t ti
1191    pub bgtime: Option<std::time::Instant>,      // c:1123 struct timespec bgtime
1192    pub endtime: Option<std::time::Instant>,     // c:1124 struct timespec endtime
1193}
1194
1195/// Port of `struct job` from `Src/zsh.h:1058-1071`.
1196///
1197/// Field-shape deviations from the C struct:
1198/// - `procs` / `auxprocs`: `Vec<process>` instead of singly-linked
1199///   `struct process *procs`. Equivalent semantics; Rust owns the
1200///   list ergonomically rather than threading `next` pointers.
1201/// - `filelist`: `Vec<String>` instead of `LinkList` of `char *`.
1202///   Same reasoning.
1203/// - `text`: added (Rust extension). C reconstructs job-display text
1204///   on demand by walking `procs->text`; the Rust port caches the
1205///   composed text here so display paths don't re-walk per call.
1206#[allow(non_camel_case_types)]
1207#[derive(Debug, Clone, Default)]
1208pub struct job {
1209    // c:1058
1210    pub gleader: i32,             // c:1059 pid_t
1211    pub other: i32,               // c:1060
1212    pub stat: i32,                // c:1062 STAT_*
1213    pub pwd: Option<String>,      // c:1063
1214    pub procs: Vec<process>,      // c:1065 struct process *procs
1215    pub auxprocs: Vec<process>,   // c:1066 struct process *auxprocs
1216    pub filelist: Vec<String>,    // c:1067 LinkList filelist
1217    pub stty_in_env: i32,         // c:1069
1218    pub ty: Option<Box<ttyinfo>>, // c:1070
1219    /// Rust extension: cached job-display text. C re-derives via
1220    /// `procs` walks in `printjob()` (`Src/jobs.c:1244+`).
1221    pub text: String,
1222}
1223
1224/// Port of `struct funcdump` from `Src/zsh.h:776-786`.
1225#[allow(non_camel_case_types)]
1226#[derive(Debug, Clone)]
1227pub struct funcdump {
1228    // c:776
1229    pub next: Option<FuncDump>,   // c:777
1230    pub dev: u64,                 // c:778 dev_t
1231    pub ino: u64,                 // c:779 ino_t
1232    pub fd: i32,                  // c:780
1233    pub map: Wordcode,            // c:781
1234    pub addr: Wordcode,           // c:782
1235    pub len: i32,                 // c:783
1236    pub count: i32,               // c:784
1237    pub filename: Option<String>, // c:785
1238}
1239
1240/// Port of `struct eprog` from `Src/zsh.h:805-815`.
1241#[allow(non_camel_case_types)]
1242#[derive(Debug, Clone, Default)]
1243pub struct eprog {
1244    // c:805
1245    pub flags: i32,             // c:806 EF_*
1246    pub len: i32,               // c:807
1247    pub npats: i32,             // c:808
1248    pub nref: i32,              // c:809
1249    pub pats: Vec<Patprog>,     // c:810
1250    pub prog: Wordcode,         // c:811
1251    pub strs: Option<String>,   // c:812
1252    pub shf: Option<Shfunc>,    // c:813
1253    pub dump: Option<FuncDump>, // c:814
1254}
1255
1256/// Port of `struct estate` from `Src/zsh.h:824-828`.
1257#[allow(non_camel_case_types)]
1258pub struct estate {
1259    // c:824
1260    pub prog: Eprog,          // c:825
1261    pub pc: usize,            // c:826 Wordcode pc — index into prog.prog (C is wordcode *)
1262    pub strs: Option<String>, // c:827 copy of prog.strs at estate creation
1263    pub strs_offset: usize,   // c:827 byte offset into strs — mirrors C `strs` pointer movement
1264}
1265
1266/// Port of `struct eccstr` from `Src/zsh.h:836-858`.
1267#[allow(non_camel_case_types)]
1268pub struct eccstr {
1269    // c:836
1270    pub left: Option<Eccstr>,  // c:838
1271    pub right: Option<Eccstr>, // c:838
1272    pub str: Option<String>,  // c:841
1273    pub offs: wordcode,        // c:844
1274    pub aoffs: wordcode,       // c:847
1275    pub nfunc: i32,            // c:854
1276    pub hashval: u32,          // c:857
1277}
1278
1279// =============================================================================
1280// 12. Z_* sublist flags (zsh.h:645-648).
1281// =============================================================================
1282
1283pub const Z_TIMED: i32 = 1 << 0; // c:645
1284pub const Z_SYNC: i32 = 1 << 1; // c:646
1285pub const Z_ASYNC: i32 = 1 << 2; // c:647
1286pub const Z_DISOWN: i32 = 1 << 3; // c:648
1287
1288// =============================================================================
1289// 13. COND_* condition types (zsh.h:660-679).
1290// =============================================================================
1291
1292pub const COND_NOT: i32 = 0;
1293pub const COND_AND: i32 = 1;
1294pub const COND_OR: i32 = 2;
1295pub const COND_STREQ: i32 = 3;
1296pub const COND_STRDEQ: i32 = 4;
1297pub const COND_STRNEQ: i32 = 5;
1298pub const COND_STRLT: i32 = 6;
1299pub const COND_STRGTR: i32 = 7;
1300pub const COND_NT: i32 = 8;
1301pub const COND_OT: i32 = 9;
1302pub const COND_EF: i32 = 10;
1303pub const COND_EQ: i32 = 11;
1304pub const COND_NE: i32 = 12;
1305pub const COND_LT: i32 = 13;
1306pub const COND_GT: i32 = 14;
1307pub const COND_LE: i32 = 15;
1308pub const COND_GE: i32 = 16;
1309pub const COND_REGEX: i32 = 17;
1310pub const COND_MOD: i32 = 18;
1311pub const COND_MODI: i32 = 19;
1312
1313pub const CONDF_INFIX: i32 = 1; // c:695
1314pub const CONDF_ADDED: i32 = 2; // c:697
1315pub const CONDF_AUTOALL: i32 = 4; // c:699
1316
1317// =============================================================================
1318// 14. Redirection structures (zsh.h:706-740) + MULTIOUNIT.
1319// =============================================================================
1320
1321pub const REDIRF_FROM_HEREDOC: i32 = 1; // c:708
1322
1323#[allow(non_camel_case_types)]
1324pub struct redir {
1325    // c:713
1326    pub typ: i32,
1327    pub flags: i32,
1328    pub fd1: i32,
1329    pub fd2: i32,
1330    pub name: Option<String>,
1331    pub varid: Option<String>,
1332    pub here_terminator: Option<String>,
1333    pub munged_here_terminator: Option<String>,
1334}
1335
1336pub const MULTIOUNIT: usize = 8; // c:725
1337
1338#[allow(non_camel_case_types)]
1339pub struct multio {
1340    // c:735
1341    pub ct: i32,
1342    pub rflag: i32,
1343    pub pipe: i32,
1344    pub fds: [i32; MULTIOUNIT],
1345}
1346
1347// =============================================================================
1348// 15. value struct (zsh.h:744-755) + VALFLAG_* + MAX_ARRLEN.
1349// =============================================================================
1350
1351#[allow(non_camel_case_types)]
1352pub struct value {
1353    // c:744
1354    pub pm: Option<Param>,
1355    pub arr: Vec<String>,
1356    pub scanflags: i32,
1357    pub valflags: i32,
1358    pub start: i32,
1359    pub end: i32,
1360}
1361
1362pub const VALFLAG_INV: i32 = 0x0001; // c:758
1363pub const VALFLAG_EMPTY: i32 = 0x0002;
1364pub const VALFLAG_SUBST: i32 = 0x0004;
1365pub const VALFLAG_REFSLICE: i32 = 0x0008;
1366
1367pub const MAX_ARRLEN: i32 = 262144; // c:764
1368
1369// =============================================================================
1370// 16. Word code types (zsh.h:770-1038).
1371// =============================================================================
1372
1373#[allow(non_camel_case_types)]
1374pub type wordcode = u32; // c:770
1375pub type Wordcode = Vec<wordcode>; // c:771
1376
1377pub type FuncDump = Box<funcdump>; // c:773
1378pub type Eprog = Box<eprog>; // c:774
1379
1380pub const EF_REAL: i32 = 1; // c:817
1381pub const EF_HEAP: i32 = 2;
1382pub const EF_MAP: i32 = 4;
1383pub const EF_RUN: i32 = 8;
1384
1385pub type Estate = Box<estate>; // c:822
1386pub type Eccstr = Box<eccstr>; // c:835
1387
1388pub const EC_NODUP: i32 = 0; // c:869
1389pub const EC_DUP: i32 = 1; // c:872
1390pub const EC_DUPTOK: i32 = 2; // c:878
1391
1392pub const WC_CODEBITS: u32 = 5; // c:882
1393#[inline]
1394#[allow(non_snake_case)]
1395pub fn wc_code(c: wordcode) -> wordcode {
1396    c & ((1 << WC_CODEBITS) - 1)
1397}
1398#[inline]
1399#[allow(non_snake_case)]
1400pub fn wc_data(c: wordcode) -> wordcode {
1401    c >> WC_CODEBITS
1402}
1403#[inline]
1404#[allow(non_snake_case)]
1405pub fn wc_bdata(d: wordcode) -> wordcode {
1406    d << WC_CODEBITS
1407}
1408#[inline]
1409#[allow(non_snake_case)]
1410pub fn wc_bld(c: wordcode, d: wordcode) -> wordcode {
1411    c | (d << WC_CODEBITS)
1412}
1413
1414pub const WC_END: wordcode = 0;
1415pub const WC_LIST: wordcode = 1;
1416pub const WC_SUBLIST: wordcode = 2;
1417pub const WC_PIPE: wordcode = 3;
1418pub const WC_REDIR: wordcode = 4;
1419pub const WC_ASSIGN: wordcode = 5;
1420pub const WC_SIMPLE: wordcode = 6;
1421pub const WC_TYPESET: wordcode = 7;
1422pub const WC_SUBSH: wordcode = 8;
1423pub const WC_CURSH: wordcode = 9;
1424pub const WC_TIMED: wordcode = 10;
1425pub const WC_FUNCDEF: wordcode = 11;
1426pub const WC_FOR: wordcode = 12;
1427pub const WC_SELECT: wordcode = 13;
1428pub const WC_WHILE: wordcode = 14;
1429pub const WC_REPEAT: wordcode = 15;
1430pub const WC_CASE: wordcode = 16;
1431pub const WC_IF: wordcode = 17;
1432pub const WC_COND: wordcode = 18;
1433pub const WC_ARITH: wordcode = 19;
1434pub const WC_AUTOFN: wordcode = 20;
1435pub const WC_TRY: wordcode = 21;
1436pub const WC_COUNT: wordcode = 22;
1437
1438pub const Z_END: i32 = 1 << 4; // c:921
1439pub const Z_SIMPLE: i32 = 1 << 5; // c:922
1440pub const WC_LIST_FREE: u32 = 6; // c:923
1441
1442pub const WC_SUBLIST_END: wordcode = 0;
1443pub const WC_SUBLIST_AND: wordcode = 1;
1444pub const WC_SUBLIST_OR: wordcode = 2;
1445pub const WC_SUBLIST_COPROC: wordcode = 4;
1446pub const WC_SUBLIST_NOT: wordcode = 8;
1447pub const WC_SUBLIST_SIMPLE: wordcode = 16;
1448pub const WC_SUBLIST_FREE: u32 = 5; // c:935
1449
1450pub const WC_PIPE_END: wordcode = 0;
1451pub const WC_PIPE_MID: wordcode = 1;
1452
1453pub const WC_ASSIGN_SCALAR: wordcode = 0;
1454pub const WC_ASSIGN_ARRAY: wordcode = 1;
1455pub const WC_ASSIGN_NEW: wordcode = 0;
1456pub const WC_ASSIGN_INC: wordcode = 1;
1457
1458pub const WC_TIMED_EMPTY: wordcode = 0;
1459pub const WC_TIMED_PIPE: wordcode = 1;
1460
1461pub const WC_FOR_PPARAM: wordcode = 0;
1462pub const WC_FOR_LIST: wordcode = 1;
1463pub const WC_FOR_COND: wordcode = 2;
1464
1465pub const WC_SELECT_PPARAM: wordcode = 0;
1466pub const WC_SELECT_LIST: wordcode = 1;
1467
1468pub const WC_WHILE_WHILE: wordcode = 0;
1469pub const WC_WHILE_UNTIL: wordcode = 1;
1470
1471pub const WC_CASE_HEAD: wordcode = 0;
1472pub const WC_CASE_OR: wordcode = 1;
1473pub const WC_CASE_AND: wordcode = 2;
1474pub const WC_CASE_TESTAND: wordcode = 3;
1475pub const WC_CASE_FREE: u32 = 3; // c:1020
1476
1477pub const WC_IF_HEAD: wordcode = 0;
1478pub const WC_IF_IF: wordcode = 1;
1479pub const WC_IF_ELIF: wordcode = 2;
1480pub const WC_IF_ELSE: wordcode = 3;
1481
1482// =============================================================================
1483// 16b. WC accessor + builder macros (zsh.h:918-1038).
1484// Each WC_X_TYPE / WC_X_SKIP / WCB_X is one of the per-opcode
1485// `wc_data` slicers / `wc_bld` constructors.
1486// =============================================================================
1487
1488#[inline]
1489#[allow(non_snake_case)]
1490pub fn WCB_END() -> wordcode {
1491    wc_bld(WC_END, 0)
1492} // c:918
1493#[inline]
1494#[allow(non_snake_case)]
1495pub fn WC_LIST_TYPE(c: wordcode) -> wordcode {
1496    wc_data(c)
1497} // c:920
1498#[inline]
1499#[allow(non_snake_case)]
1500pub fn WC_LIST_SKIP(c: wordcode) -> wordcode {
1501    wc_data(c) >> WC_LIST_FREE
1502} // c:924
1503#[inline]
1504#[allow(non_snake_case)]
1505pub fn WCB_LIST(t: wordcode, o: wordcode) -> wordcode {
1506    wc_bld(WC_LIST, t | (o << WC_LIST_FREE))
1507}
1508#[inline]
1509#[allow(non_snake_case)]
1510pub fn WC_SUBLIST_TYPE(c: wordcode) -> wordcode {
1511    wc_data(c) & 3
1512} // c:927
1513#[inline]
1514#[allow(non_snake_case)]
1515pub fn WC_SUBLIST_FLAGS(c: wordcode) -> wordcode {
1516    wc_data(c) & 0x1c
1517} // c:931
1518#[inline]
1519#[allow(non_snake_case)]
1520pub fn WC_SUBLIST_SKIP(c: wordcode) -> wordcode {
1521    wc_data(c) >> WC_SUBLIST_FREE
1522}
1523#[inline]
1524#[allow(non_snake_case)]
1525pub fn WCB_SUBLIST(t: wordcode, f: wordcode, o: wordcode) -> wordcode {
1526    wc_bld(WC_SUBLIST, t | f | (o << WC_SUBLIST_FREE))
1527}
1528#[inline]
1529#[allow(non_snake_case)]
1530pub fn WC_PIPE_TYPE(c: wordcode) -> wordcode {
1531    wc_data(c) & 1
1532} // c:940
1533#[inline]
1534#[allow(non_snake_case)]
1535pub fn WC_PIPE_LINENO(c: wordcode) -> wordcode {
1536    wc_data(c) >> 1
1537}
1538#[inline]
1539#[allow(non_snake_case)]
1540pub fn WCB_PIPE(t: wordcode, l: wordcode) -> wordcode {
1541    wc_bld(WC_PIPE, t | (l << 1))
1542}
1543#[inline]
1544#[allow(non_snake_case)]
1545pub fn WC_REDIR_TYPE(c: wordcode) -> i32 {
1546    (wc_data(c) & REDIR_TYPE_MASK as u32) as i32
1547}
1548#[inline]
1549#[allow(non_snake_case)]
1550pub fn WC_REDIR_VARID(c: wordcode) -> i32 {
1551    (wc_data(c) & REDIR_VARID_MASK as u32) as i32
1552}
1553#[inline]
1554#[allow(non_snake_case)]
1555pub fn WC_REDIR_FROM_HEREDOC(c: wordcode) -> i32 {
1556    (wc_data(c) & REDIR_FROM_HEREDOC_MASK as u32) as i32
1557}
1558#[inline]
1559#[allow(non_snake_case)]
1560pub fn WCB_REDIR(t: wordcode) -> wordcode {
1561    wc_bld(WC_REDIR, t)
1562}
1563#[inline]
1564#[allow(non_snake_case)]
1565pub fn WC_REDIR_WORDS(c: wordcode) -> i32 {
1566    (if WC_REDIR_VARID(c) != 0 { 4 } else { 3 })
1567        + (if WC_REDIR_FROM_HEREDOC(c) != 0 { 2 } else { 0 })
1568}
1569#[inline]
1570#[allow(non_snake_case)]
1571pub fn WC_ASSIGN_TYPE(c: wordcode) -> wordcode {
1572    wc_data(c) & 1
1573} // c:955
1574#[inline]
1575#[allow(non_snake_case)]
1576pub fn WC_ASSIGN_TYPE2(c: wordcode) -> wordcode {
1577    (wc_data(c) & 2) >> 1
1578}
1579#[inline]
1580#[allow(non_snake_case)]
1581pub fn WC_ASSIGN_NUM(c: wordcode) -> wordcode {
1582    wc_data(c) >> 2
1583}
1584#[inline]
1585#[allow(non_snake_case)]
1586pub fn WCB_ASSIGN(t: wordcode, a: wordcode, n: wordcode) -> wordcode {
1587    wc_bld(WC_ASSIGN, t | (a << 1) | (n << 2))
1588}
1589#[inline]
1590#[allow(non_snake_case)]
1591pub fn WC_SIMPLE_ARGC(c: wordcode) -> wordcode {
1592    wc_data(c)
1593} // c:970
1594#[inline]
1595#[allow(non_snake_case)]
1596pub fn WCB_SIMPLE(n: wordcode) -> wordcode {
1597    wc_bld(WC_SIMPLE, n)
1598}
1599#[inline]
1600#[allow(non_snake_case)]
1601pub fn WC_TYPESET_ARGC(c: wordcode) -> wordcode {
1602    wc_data(c)
1603} // c:973
1604#[inline]
1605#[allow(non_snake_case)]
1606pub fn WCB_TYPESET(n: wordcode) -> wordcode {
1607    wc_bld(WC_TYPESET, n)
1608}
1609#[inline]
1610#[allow(non_snake_case)]
1611pub fn WC_SUBSH_SKIP(c: wordcode) -> wordcode {
1612    wc_data(c)
1613} // c:976
1614#[inline]
1615#[allow(non_snake_case)]
1616pub fn WCB_SUBSH(o: wordcode) -> wordcode {
1617    wc_bld(WC_SUBSH, o)
1618}
1619#[inline]
1620#[allow(non_snake_case)]
1621pub fn WC_CURSH_SKIP(c: wordcode) -> wordcode {
1622    wc_data(c)
1623} // c:979
1624#[inline]
1625#[allow(non_snake_case)]
1626pub fn WCB_CURSH(o: wordcode) -> wordcode {
1627    wc_bld(WC_CURSH, o)
1628}
1629#[inline]
1630#[allow(non_snake_case)]
1631pub fn WC_TIMED_TYPE(c: wordcode) -> wordcode {
1632    wc_data(c)
1633} // c:982
1634#[inline]
1635#[allow(non_snake_case)]
1636pub fn WCB_TIMED(t: wordcode) -> wordcode {
1637    wc_bld(WC_TIMED, t)
1638}
1639#[inline]
1640#[allow(non_snake_case)]
1641pub fn WC_FUNCDEF_SKIP(c: wordcode) -> wordcode {
1642    wc_data(c)
1643} // c:987
1644#[inline]
1645#[allow(non_snake_case)]
1646pub fn WCB_FUNCDEF(o: wordcode) -> wordcode {
1647    wc_bld(WC_FUNCDEF, o)
1648}
1649#[inline]
1650#[allow(non_snake_case)]
1651pub fn WC_FOR_TYPE(c: wordcode) -> wordcode {
1652    wc_data(c) & 3
1653} // c:990
1654#[inline]
1655#[allow(non_snake_case)]
1656pub fn WC_FOR_SKIP(c: wordcode) -> wordcode {
1657    wc_data(c) >> 2
1658}
1659#[inline]
1660#[allow(non_snake_case)]
1661pub fn WCB_FOR(t: wordcode, o: wordcode) -> wordcode {
1662    wc_bld(WC_FOR, t | (o << 2))
1663}
1664#[inline]
1665#[allow(non_snake_case)]
1666pub fn WC_SELECT_TYPE(c: wordcode) -> wordcode {
1667    wc_data(c) & 1
1668} // c:997
1669#[inline]
1670#[allow(non_snake_case)]
1671pub fn WC_SELECT_SKIP(c: wordcode) -> wordcode {
1672    wc_data(c) >> 1
1673}
1674#[inline]
1675#[allow(non_snake_case)]
1676pub fn WCB_SELECT(t: wordcode, o: wordcode) -> wordcode {
1677    wc_bld(WC_SELECT, t | (o << 1))
1678}
1679#[inline]
1680#[allow(non_snake_case)]
1681pub fn WC_WHILE_TYPE(c: wordcode) -> wordcode {
1682    wc_data(c) & 1
1683} // c:1003
1684#[inline]
1685#[allow(non_snake_case)]
1686pub fn WC_WHILE_SKIP(c: wordcode) -> wordcode {
1687    wc_data(c) >> 1
1688}
1689#[inline]
1690#[allow(non_snake_case)]
1691pub fn WCB_WHILE(t: wordcode, o: wordcode) -> wordcode {
1692    wc_bld(WC_WHILE, t | (o << 1))
1693}
1694#[inline]
1695#[allow(non_snake_case)]
1696pub fn WC_REPEAT_SKIP(c: wordcode) -> wordcode {
1697    wc_data(c)
1698} // c:1009
1699#[inline]
1700#[allow(non_snake_case)]
1701pub fn WCB_REPEAT(o: wordcode) -> wordcode {
1702    wc_bld(WC_REPEAT, o)
1703}
1704#[inline]
1705#[allow(non_snake_case)]
1706pub fn WC_TRY_SKIP(c: wordcode) -> wordcode {
1707    wc_data(c)
1708} // c:1012
1709#[inline]
1710#[allow(non_snake_case)]
1711pub fn WCB_TRY(o: wordcode) -> wordcode {
1712    wc_bld(WC_TRY, o)
1713}
1714#[inline]
1715#[allow(non_snake_case)]
1716pub fn WC_CASE_TYPE(c: wordcode) -> wordcode {
1717    wc_data(c) & 7
1718} // c:1015
1719#[inline]
1720#[allow(non_snake_case)]
1721pub fn WC_CASE_SKIP(c: wordcode) -> wordcode {
1722    wc_data(c) >> WC_CASE_FREE
1723}
1724#[inline]
1725#[allow(non_snake_case)]
1726pub fn WCB_CASE(t: wordcode, o: wordcode) -> wordcode {
1727    wc_bld(WC_CASE, t | (o << WC_CASE_FREE))
1728}
1729#[inline]
1730#[allow(non_snake_case)]
1731pub fn WC_IF_TYPE(c: wordcode) -> wordcode {
1732    wc_data(c) & 3
1733} // c:1024
1734#[inline]
1735#[allow(non_snake_case)]
1736pub fn WC_IF_SKIP(c: wordcode) -> wordcode {
1737    wc_data(c) >> 2
1738}
1739#[inline]
1740#[allow(non_snake_case)]
1741pub fn WCB_IF(t: wordcode, o: wordcode) -> wordcode {
1742    wc_bld(WC_IF, t | (o << 2))
1743}
1744#[inline]
1745#[allow(non_snake_case)]
1746pub fn WC_COND_TYPE(c: wordcode) -> wordcode {
1747    wc_data(c) & 127
1748} // c:1032
1749#[inline]
1750#[allow(non_snake_case)]
1751pub fn WC_COND_SKIP(c: wordcode) -> wordcode {
1752    wc_data(c) >> 7
1753}
1754#[inline]
1755#[allow(non_snake_case)]
1756pub fn WCB_COND(t: wordcode, o: wordcode) -> wordcode {
1757    wc_bld(WC_COND, t | (o << 7))
1758}
1759#[inline]
1760#[allow(non_snake_case)]
1761pub fn WCB_ARITH() -> wordcode {
1762    wc_bld(WC_ARITH, 0)
1763} // c:1036
1764#[inline]
1765#[allow(non_snake_case)]
1766pub fn WCB_AUTOFN() -> wordcode {
1767    wc_bld(WC_AUTOFN, 0)
1768} // c:1038
1769
1770// =============================================================================
1771// 16c. Other macros: BUILTIN/BIN_PREFIX/CONDDEF/HOOKDEF/PARAMDEF/etc.
1772// =============================================================================
1773
1774/// Port of `#define NULLBINCMD` from `Src/zsh.h:1438`.
1775pub const NULLBINCMD: Option<HandlerFunc> = None; // c:1438
1776
1777/// Port of `#define EMULATION(X)` from `Src/zsh.h:2347`.
1778/// C macro: `(emulation & (X))`. Reads the canonical `emulation`
1779/// static from `crate::ported::options::emulation` directly.
1780#[inline]
1781#[allow(non_snake_case)]
1782pub fn EMULATION(x: i32) -> bool {                                           // c:2347
1783    let emul = crate::ported::options::emulation
1784        .load(std::sync::atomic::Ordering::Relaxed);
1785    (emul & x) != 0
1786}
1787
1788/// Port of `#define SHELL_EMULATION()` from `Src/zsh.h:2350`.
1789/// C macro: `(emulation & ((1<<5)-1))`. Reads the canonical
1790/// `emulation` static directly.
1791#[inline]
1792#[allow(non_snake_case)]
1793pub fn SHELL_EMULATION() -> i32 {                                            // c:2350
1794    let emul = crate::ported::options::emulation
1795        .load(std::sync::atomic::Ordering::Relaxed);
1796    emul & ((1 << 5) - 1)
1797}
1798
1799/// Port of `#define IN_EVAL_TRAP()` from `Src/zsh.h:2962`.
1800/// C macro reads the four globals `intrap` / `trapisfunc` /
1801/// `traplocallevel` / `locallevel` directly with no args; Rust
1802/// matches by reading the canonical statics
1803/// (`signals::intrap`, `signals::trapisfunc`,
1804/// `signals::traplocallevel`, `params::locallevel`) inside.
1805#[inline]
1806#[allow(non_snake_case)]
1807pub fn IN_EVAL_TRAP() -> bool {                                              // c:2962
1808    crate::ported::signals::intrap.load(Ordering::Relaxed) != 0
1809        && crate::ported::signals::trapisfunc.load(Ordering::Relaxed) == 0
1810        && crate::ported::signals::traplocallevel.load(Ordering::Relaxed)
1811            == crate::ported::params::locallevel.load(Ordering::Relaxed)
1812}
1813
1814/// Port of `#define ASG_ARRAYP(asg)` from `Src/zsh.h:1288`.
1815#[inline]
1816#[allow(non_snake_case)]
1817pub fn ASG_ARRAYP(asg: &asgment) -> bool {
1818    (asg.flags & ASG_ARRAY) != 0
1819}
1820
1821/// Port of `#define ASG_VALUEP(asg)` from `Src/zsh.h:1296`.
1822#[inline]
1823#[allow(non_snake_case)]
1824pub fn ASG_VALUEP(asg: &asgment) -> bool {
1825    ASG_ARRAYP(asg) || asg.scalar.is_some()
1826}
1827
1828/// Port of `#define MB_METASTRLEN2END(str, widthp, eptr)` from
1829/// `Src/zsh.h:3282/3363`. C: `mb_metastrlenend(str, widthp, eptr)`
1830/// (multibyte) or `ztrlenend(str, eptr)` (non-multibyte). Rust port
1831/// counts metafied chars from `str` up to `eptr` (exclusive).
1832#[inline]
1833#[allow(non_snake_case)]
1834pub fn MB_METASTRLEN2END(s: &str, widthp: bool, eptr: usize) -> usize {
1835    let truncated = if eptr <= s.len() { &s[..eptr] } else { s };
1836    MB_METASTRLEN2(truncated, widthp)
1837}
1838
1839// Hook-table indices (zsh.h:3259-3262). C: `(zshhooks + N)` —
1840// the Rust port exposes the offsets; consumers index into the
1841// `zshhooks[]` array themselves.
1842pub const EXITHOOK_OFFSET: usize = 0; // c:3259
1843pub const BEFORETRAPHOOK_OFFSET: usize = 1; // c:3260
1844pub const AFTERTRAPHOOK_OFFSET: usize = 2; // c:3261
1845pub const GETCOLORATTR_OFFSET: usize = 3; // c:3262
1846
1847/// Port of `#define STOPHIST` from `Src/zsh.h:2267`. Increments the
1848/// `stophist` global by 4. Rust port exposes the delta; the global
1849/// itself lives in `hist.rs`.
1850pub const STOPHIST_DELTA: i32 = 4; // c:2267
1851pub const ALLOWHIST_DELTA: i32 = -4; // c:2268
1852
1853/// Aliases under the canonical C macro names. C uses these in
1854/// statement-style: `STOPHIST` and `ALLOWHIST` expand to assignments
1855/// modifying the global; Rust port exposes them as the deltas.
1856pub const STOPHIST: i32 = STOPHIST_DELTA;
1857pub const ALLOWHIST: i32 = ALLOWHIST_DELTA;
1858
1859/// Hook-table indices under their canonical zsh.h names (C: `(zshhooks
1860/// + N)`).
1861pub const EXITHOOK: usize = EXITHOOK_OFFSET;
1862pub const BEFORETRAPHOOK: usize = BEFORETRAPHOOK_OFFSET;
1863pub const AFTERTRAPHOOK: usize = AFTERTRAPHOOK_OFFSET;
1864pub const GETCOLORATTR: usize = GETCOLORATTR_OFFSET;
1865
1866/// Port of `#define ZLONG_CONST(x)` from `Src/zsh.h:68/72/78/83`.
1867/// C casts an integer literal to `zlong` via the `l`/`ll` suffix.
1868/// In Rust integer literals are typed at the use site; this macro
1869/// is an explicit cast to `zlong` (= `i64`).
1870#[inline]
1871#[allow(non_snake_case)]
1872pub const fn ZLONG_CONST(x: i64) -> zlong {
1873    x
1874} // c:68
1875
1876/// Port of `#define STRINGIFY_LITERAL(x)` from `Src/zsh.h:2915`. C
1877/// uses the `#` operator to stringify an identifier. Rust's
1878/// `stringify!` macro does the same.
1879#[macro_export]
1880macro_rules! STRINGIFY_LITERAL {
1881    ($x:tt) => {
1882        stringify!($x)
1883    };
1884}
1885
1886/// Port of `#define STRINGIFY(x)` from `Src/zsh.h:2916`. Two-pass
1887/// stringification (expand x first, then stringify).
1888#[macro_export]
1889macro_rules! STRINGIFY {
1890    ($x:tt) => {
1891        $crate::STRINGIFY_LITERAL!($x)
1892    };
1893}
1894
1895/// Port of `#define ERRMSG(x)` from `Src/zsh.h:2917`. Build a debug
1896/// error-message prefix `__FILE__ ":" __LINE__ ": " x`.
1897#[macro_export]
1898macro_rules! ERRMSG {
1899    ($msg:expr) => {
1900        concat!(file!(), ":", line!(), ": ", $msg)
1901    };
1902}
1903
1904/// Port of `#define HEAPID_FMT` from `Src/zsh.h:2831`. printf format
1905/// specifier for `Heapid` values. C uses `"%x"`; Rust uses `"{:x}"`.
1906pub const HEAPID_FMT: &str = "{:x}"; // c:2831
1907
1908/// Port of `#define HEAP_ERROR(heap_id)` from `Src/zsh.h:2864`. Debug-
1909/// only macro that fprintf's an "invalid heap" error to stderr.
1910/// Rust port: eprintln! with the same format. Only active under
1911/// the `zsh-heap-debug` feature.
1912#[macro_export]
1913macro_rules! HEAP_ERROR {
1914    ($heap_id:expr) => {
1915        eprintln!(
1916            "{}:{}: HEAP DEBUG: invalid heap: {:x}.",
1917            file!(),
1918            line!(),
1919            $heap_id
1920        )
1921    };
1922}
1923
1924/// Port of `#define SGTTYFLAG` from `Src/zsh.h:2614/2616`. Termios
1925/// flag accessor — `shttyinfo.tio.c_oflag` (HAVE_TERMIOS) or
1926/// `shttyinfo.sgttyb.sg_flags` (sgtty fallback). Rust port exposes
1927/// the field name; consumers access via `&ttyinfo.tio.c_oflag`.
1928pub const SGTTYFLAG_NAME: &str = "tio.c_oflag";
1929
1930/// Canonical alias under the C macro name (consumers reference it
1931/// in error messages / debug output).
1932pub const SGTTYFLAG: &str = SGTTYFLAG_NAME;
1933
1934/// Port of `#define SGTABTYPE` from `Src/zsh.h:2619/2622/2625`.
1935/// Tab-expansion mode constant — `TAB3` / `OXTABS` / `XTABS` per
1936/// platform. macOS/BSD use `OXTABS`; Linux uses `XTABS`.
1937#[cfg(target_os = "linux")]
1938pub const SGTABTYPE: u32 = libc::XTABS;
1939
1940#[cfg(not(target_os = "linux"))]
1941pub const SGTABTYPE: u32 = 0;
1942
1943/// Port of `#define ZWS(s)` from `Src/zsh.h:3329/3373`. Wide-string
1944/// cast. In Rust `&str` is already UTF-8; pass through.
1945#[inline]
1946#[allow(non_snake_case)]
1947pub fn ZWS(s: &str) -> &str {
1948    s
1949}
1950
1951// =============================================================================
1952// 16d. BUILTIN / BIN_PREFIX / CONDDEF / HOOKDEF / NUMMATHFUNC /
1953// STRMATHFUNC / PARAMDEF / INTPARAMDEF / STRPARAMDEF / ARRPARAMDEF /
1954// SPECIALPMDEF / WRAPDEF — table-row builder macros (zsh.h:1450-2125).
1955// These build initialiser literals for the various per-table arrays.
1956// Rust ports as `const fn`-equivalent constructors returning the
1957// matching struct.
1958// =============================================================================
1959
1960/// Port of `BUILTIN(name, flags, handler, min, max, funcid, optstr, defopts)`
1961/// from `Src/zsh.h:1450`.
1962#[inline]
1963#[allow(non_snake_case)]
1964pub fn BUILTIN(
1965    name: &str,
1966    flags: i32,
1967    handler: Option<HandlerFunc>,
1968    min: i32,
1969    max: i32,
1970    funcid: i32,
1971    optstr: Option<&str>,
1972    defopts: Option<&str>,
1973) -> builtin {
1974    builtin {
1975        node: hashnode {
1976            next: None,
1977            nam: name.to_string(),
1978            flags,
1979        },
1980        handlerfunc: handler,
1981        minargs: min,
1982        maxargs: max,
1983        funcid,
1984        optstr: optstr.map(|s| s.to_string()),
1985        defopts: defopts.map(|s| s.to_string()),
1986    }
1987}
1988
1989/// Port of `BIN_PREFIX(name, flags)` from `Src/zsh.h:1452`. Builds a
1990/// prefix-builtin entry (no handler, marked with BINF_PREFIX).
1991#[inline]
1992#[allow(non_snake_case)]
1993pub fn BIN_PREFIX(name: &str, flags: i32) -> builtin {
1994    BUILTIN(
1995        name,
1996        flags | BINF_PREFIX as i32,
1997        NULLBINCMD,
1998        0,
1999        0,
2000        0,
2001        None,
2002        None,
2003    )
2004}
2005
2006/// Port of `CONDDEF(name, flags, handler, min, max, condid)` from
2007/// `Src/zsh.h:701`.
2008#[inline]
2009#[allow(non_snake_case)]
2010pub fn CONDDEF(
2011    name: &str,
2012    flags: i32,
2013    handler: CondHandler,
2014    min: i32,
2015    max: i32,
2016    condid: i32,
2017) -> conddef {
2018    conddef {
2019        next: None,
2020        name: name.to_string(),
2021        flags,
2022        handler: Some(handler),
2023        min,
2024        max,
2025        condid,
2026        module: None,
2027    }
2028}
2029
2030/// Port of `HOOKDEF(name, func, flags)` from `Src/zsh.h:1594`.
2031#[inline]
2032#[allow(non_snake_case)]
2033pub fn HOOKDEF(name: &str, func: Hookfn, flags: i32) -> hookdef {
2034    hookdef {
2035        next: None,
2036        name: name.to_string(),
2037        def: Some(func),
2038        flags,
2039        funcs: None,
2040    }
2041}
2042
2043/// Port of `NUMMATHFUNC(name, func, min, max, id)` from `Src/zsh.h:133`.
2044#[inline]
2045#[allow(non_snake_case)]
2046pub fn NUMMATHFUNC(name: &str, func: NumMathFunc, min: i32, max: i32, id: i32) -> mathfunc {
2047    mathfunc {
2048        next: None,
2049        name: name.to_string(),
2050        flags: 0,
2051        nfunc: Some(func),
2052        sfunc: None,
2053        module: None,
2054        minargs: min,
2055        maxargs: max,
2056        funcid: id,
2057    }
2058}
2059
2060/// Port of `STRMATHFUNC(name, func, id)` from `Src/zsh.h:135`.
2061#[inline]
2062#[allow(non_snake_case)]
2063pub fn STRMATHFUNC(name: &str, func: StrMathFunc, id: i32) -> mathfunc {
2064    mathfunc {
2065        next: None,
2066        name: name.to_string(),
2067        flags: MFF_STR,
2068        nfunc: None,
2069        sfunc: Some(func),
2070        module: None,
2071        minargs: 0,
2072        maxargs: 0,
2073        funcid: id,
2074    }
2075}
2076
2077/// Port of `PARAMDEF(name, flags, var, gsu)` from `Src/zsh.h:2096`.
2078#[inline]
2079#[allow(non_snake_case)]
2080pub fn PARAMDEF(name: &str, flags: i32, var: usize, gsu: usize) -> paramdef {
2081    paramdef {
2082        name: name.to_string(),
2083        flags,
2084        var,
2085        gsu,
2086        getnfn: None,
2087        scantfn: None,
2088        pm: None,
2089    }
2090}
2091
2092/// Port of `INTPARAMDEF(name, var)` from `Src/zsh.h:2105`.
2093#[inline]
2094#[allow(non_snake_case)]
2095pub fn INTPARAMDEF(name: &str, var: usize) -> paramdef {
2096    PARAMDEF(name, PM_INTEGER as i32, var, 0)
2097}
2098
2099/// Port of `STRPARAMDEF(name, var)` from `Src/zsh.h:2107`.
2100#[inline]
2101#[allow(non_snake_case)]
2102pub fn STRPARAMDEF(name: &str, var: usize) -> paramdef {
2103    PARAMDEF(name, PM_SCALAR as i32, var, 0)
2104}
2105
2106/// Port of `ARRPARAMDEF(name, var)` from `Src/zsh.h:2109`.
2107#[inline]
2108#[allow(non_snake_case)]
2109pub fn ARRPARAMDEF(name: &str, var: usize) -> paramdef {
2110    PARAMDEF(name, PM_ARRAY as i32, var, 0)
2111}
2112
2113/// Port of `SPECIALPMDEF(name, flags, gsufn, getfn, scanfn)` from
2114/// `Src/zsh.h:2123`.
2115#[inline]
2116#[allow(non_snake_case)]
2117pub fn SPECIALPMDEF(
2118    name: &str,
2119    flags: i32,
2120    gsufn: usize,
2121    getfn: Option<GetNodeFunc>,
2122    scanfn: Option<ScanTabFunc>,
2123) -> paramdef {
2124    paramdef {
2125        name: name.to_string(),
2126        flags: flags | (PM_SPECIAL | PM_HIDE | PM_HIDEVAL) as i32,
2127        var: 0,
2128        gsu: gsufn,
2129        getnfn: getfn,
2130        scantfn: scanfn,
2131        pm: None,
2132    }
2133}
2134/// Port of `WRAPDEF(func)` from `Src/zsh.h:1371`.
2135#[inline]
2136#[allow(non_snake_case)]
2137pub fn WRAPDEF(func: WrapFunc) -> funcwrap {
2138    funcwrap {
2139        next: None,
2140        flags: 0,
2141        handler: Some(func),
2142        module: None,
2143    }
2144}
2145
2146// =============================================================================
2147// 17. Job structures (zsh.h:1046-1166).
2148// =============================================================================
2149
2150#[allow(non_camel_case_types)]
2151pub struct jobfile {
2152    // c:1046
2153    pub name: Option<String>,
2154    pub fd: i32,
2155    pub is_fd: i32,
2156}
2157
2158pub const STAT_CHANGED: i32 = 0x0001; // c:1073
2159pub const STAT_STOPPED: i32 = 0x0002;
2160pub const STAT_TIMED: i32 = 0x0004;
2161pub const STAT_DONE: i32 = 0x0008;
2162pub const STAT_LOCKED: i32 = 0x0010;
2163pub const STAT_NOPRINT: i32 = 0x0020;
2164pub const STAT_INUSE: i32 = 0x0040;
2165pub const STAT_SUPERJOB: i32 = 0x0080;
2166pub const STAT_SUBJOB: i32 = 0x0100;
2167pub const STAT_WASSUPER: i32 = 0x0200;
2168pub const STAT_CURSH: i32 = 0x0400;
2169pub const STAT_NOSTTY: i32 = 0x0800;
2170pub const STAT_ATTACH: i32 = 0x1000;
2171pub const STAT_SUBLEADER: i32 = 0x2000;
2172pub const STAT_BUILTIN: i32 = 0x4000;
2173pub const STAT_SUBJOB_ORPHANED: i32 = 0x8000;
2174pub const STAT_DISOWN: i32 = 0x10000; // c:1095
2175
2176pub const SP_RUNNING: i32 = -1; // c:1097
2177
2178pub const JOBTEXTSIZE: usize = 80; // c:1104
2179pub const MAXJOBS_ALLOC: i32 = 50; // c:1107
2180pub const MAX_PIPESTATS: usize = 256; // c:1166
2181
2182#[allow(non_camel_case_types)]
2183#[derive(Debug, Clone, Default)]
2184pub struct timeinfo {
2185    // c:1099
2186    pub ut: i64,
2187    pub st: i64,
2188}
2189
2190impl timeinfo {
2191    pub fn user_dur(&self) -> std::time::Duration { std::time::Duration::from_micros(self.ut as u64) }
2192    pub fn sys_dur(&self)  -> std::time::Duration { std::time::Duration::from_micros(self.st as u64) }
2193}
2194
2195// =============================================================================
2196// 18. Hash table types (zsh.h:1172-1235) — DISABLED.
2197// =============================================================================
2198
2199pub const DISABLED: i32 = 1 << 0; // c:1235
2200
2201// =============================================================================
2202// 19. Alias / asgment / cmdnam / shfunc / funcstack flags + macros.
2203// =============================================================================
2204
2205pub const HASHED: i32 = 1 << 1; // c:1312
2206pub const ALIAS_GLOBAL: i32 = 1 << 1; // c:1261
2207pub const ALIAS_SUFFIX: i32 = 1 << 2; // c:1263
2208
2209pub const ASG_ARRAY: i32 = 1; // c:1280
2210pub const ASG_KEY_VALUE: i32 = 2; // c:1282
2211
2212pub const SFC_NONE: i32 = 0; // c:1329
2213pub const SFC_DIRECT: i32 = 1;
2214pub const SFC_SIGNAL: i32 = 2;
2215pub const SFC_HOOK: i32 = 3;
2216pub const SFC_WIDGET: i32 = 4;
2217pub const SFC_COMPLETE: i32 = 5;
2218pub const SFC_CWIDGET: i32 = 6;
2219pub const SFC_SUBST: i32 = 7;
2220
2221pub const FS_SOURCE: i32 = 0; // c:1341
2222pub const FS_FUNC: i32 = 1;
2223pub const FS_EVAL: i32 = 2;
2224
2225pub const WRAPF_ADDED: i32 = 1; // c:1369
2226
2227pub const HOOK_SUFFIX: &str = "_functions"; // c:1379
2228pub const HOOK_SUFFIX_LEN: usize = 11; // c:1381
2229
2230// =============================================================================
2231// 20. Options struct + MAX_OPS + OPT_* macros (zsh.h:1396-1427).
2232// =============================================================================
2233
2234pub const MAX_OPS: usize = 128; // c:1396
2235
2236#[allow(non_camel_case_types)]
2237#[derive(Clone)]
2238pub struct options {
2239    // c:1416
2240    pub ind: [u8; MAX_OPS],
2241    pub args: Vec<String>,
2242    pub argscount: i32,
2243    pub argsalloc: i32,
2244}
2245
2246pub const PARSEARGS_TOPLEVEL: i32 = 0x1; // c:1425
2247pub const PARSEARGS_LOGIN: i32 = 0x2; // c:1426
2248
2249// Port of OPT_* macros from Src/zsh.h:1400-1414. Each takes
2250// `Options ops` (= `struct options *`) and a char index. The Rust
2251// port takes `&options` (a reference to the struct ported above) and
2252// indexes `ind[c]`. Char indexing is direct (not c-1) per zsh.h:1408
2253// `((ops)->ind[c] != 0)`.
2254
2255/// Port of `OPT_MINUS(ops,c)` from `Src/zsh.h:1400` —
2256/// `((ops)->ind[c] & 1)`. True if option was set as `-X`.
2257#[inline]
2258#[allow(non_snake_case)]
2259pub fn OPT_MINUS(ops: &options, c: u8) -> bool {
2260    (ops.ind[c as usize] & 1) != 0
2261}
2262
2263/// Port of `OPT_PLUS(ops,c)` from `Src/zsh.h:1402` —
2264/// `((ops)->ind[c] & 2)`. True if option was set as `+X`.
2265#[inline]
2266#[allow(non_snake_case)]
2267pub fn OPT_PLUS(ops: &options, c: u8) -> bool {
2268    (ops.ind[c as usize] & 2) != 0
2269}
2270
2271/// Port of `OPT_ISSET(ops,c)` from `Src/zsh.h:1408` —
2272/// `((ops)->ind[c] != 0)`. True if option was set any way.
2273#[inline]
2274#[allow(non_snake_case)]
2275pub fn OPT_ISSET(ops: &options, c: u8) -> bool {
2276    ops.ind[c as usize] != 0
2277}
2278
2279/// Port of `OPT_HASARG(ops,c)` from `Src/zsh.h:1410` —
2280/// `((ops)->ind[c] > 3)`. True if option carries an argument.
2281#[inline]
2282#[allow(non_snake_case)]
2283pub fn OPT_HASARG(ops: &options, c: u8) -> bool {
2284    ops.ind[c as usize] > 3
2285}
2286
2287// =============================================================================
2288// 21. Builtin types + BINF_* (zsh.h:1436-1486).
2289// =============================================================================
2290
2291pub type HandlerFunc = fn(name: &str, args: &[String], ops: &options, funcid: i32) -> i32;
2292
2293pub const BINF_PLUSOPTS:        u32 = 1 << 1;                            // c:1457
2294pub const BINF_PRINTOPTS:       u32 = 1 << 2;                            // c:1458
2295pub const BINF_ADDED:           u32 = 1 << 3;                            // c:1459
2296pub const BINF_MAGICEQUALS:     u32 = 1 << 4;                            // c:1460
2297pub const BINF_PREFIX:          u32 = 1 << 5;                            // c:1461
2298pub const BINF_DASH:            u32 = 1 << 6;                            // c:1462
2299pub const BINF_BUILTIN:         u32 = 1 << 7;                            // c:1463
2300pub const BINF_COMMAND:         u32 = 1 << 8;                            // c:1464
2301pub const BINF_EXEC:            u32 = 1 << 9;                            // c:1465
2302pub const BINF_NOGLOB:          u32 = 1 << 10;                           // c:1466
2303pub const BINF_PSPECIAL:        u32 = 1 << 11;                           // c:1467
2304pub const BINF_SKIPINVALID:     u32 = 1 << 12;                           // c:1469
2305pub const BINF_KEEPNUM:         u32 = 1 << 13;                           // c:1470
2306pub const BINF_SKIPDASH:        u32 = 1 << 14;                           // c:1471
2307pub const BINF_DASHDASHVALID:   u32 = 1 << 15;                           // c:1472
2308pub const BINF_CLEARENV:        u32 = 1 << 16;                           // c:1473
2309pub const BINF_AUTOALL:         u32 = 1 << 17;                           // c:1474
2310pub const BINF_HANDLES_OPTS:    u32 = 1 << 18;                           // c:1480
2311pub const BINF_ASSIGN:          u32 = 1 << 19;                           // c:1486
2312
2313// =============================================================================
2314// 22. Module flags (zsh.h:1516-1532).
2315// =============================================================================
2316
2317pub const MOD_BUSY: i32 = 1 << 0; // c:1516
2318pub const MOD_UNLOAD: i32 = 1 << 1; // c:1522
2319pub const MOD_SETUP: i32 = 1 << 2; // c:1524
2320pub const MOD_LINKED: i32 = 1 << 3; // c:1526
2321pub const MOD_INIT_S: i32 = 1 << 4; // c:1528
2322pub const MOD_INIT_B: i32 = 1 << 5; // c:1530
2323pub const MOD_ALIAS: i32 = 1 << 6; // c:1532
2324
2325pub const HOOKF_ALL: i32 = 1; // c:1592
2326
2327// =============================================================================
2328// 23. Pattern flags (zsh.h:1624-1637).
2329// =============================================================================
2330
2331pub const PAT_HEAPDUP: i32 = 0x0000; // c:1624
2332pub const PAT_FILE: i32 = 0x0001;
2333pub const PAT_FILET: i32 = 0x0002;
2334pub const PAT_ANY: i32 = 0x0004;
2335pub const PAT_NOANCH: i32 = 0x0008;
2336pub const PAT_NOGLD: i32 = 0x0010;
2337pub const PAT_PURES: i32 = 0x0020;
2338pub const PAT_STATIC: i32 = 0x0040;
2339pub const PAT_SCAN: i32 = 0x0080;
2340pub const PAT_ZDUP: i32 = 0x0100;
2341pub const PAT_NOTSTART: i32 = 0x0200;
2342pub const PAT_NOTEND: i32 = 0x0400;
2343pub const PAT_HAS_EXCLUDP: i32 = 0x0800;
2344pub const PAT_LCMATCHUC: i32 = 0x1000;
2345
2346// =============================================================================
2347// 24. zpc_chars enum (zsh.h:1643-1676).
2348// =============================================================================
2349
2350pub const ZPC_SLASH: i32 = 0;
2351pub const ZPC_NULL: i32 = 1;
2352pub const ZPC_BAR: i32 = 2;
2353pub const ZPC_OUTPAR: i32 = 3;
2354pub const ZPC_TILDE: i32 = 4;
2355pub const ZPC_SEG_COUNT: i32 = 5;
2356pub const ZPC_INPAR: i32 = ZPC_SEG_COUNT;
2357pub const ZPC_QUEST: i32 = ZPC_SEG_COUNT + 1;
2358pub const ZPC_STAR: i32 = ZPC_SEG_COUNT + 2;
2359pub const ZPC_INBRACK: i32 = ZPC_SEG_COUNT + 3;
2360pub const ZPC_INANG: i32 = ZPC_SEG_COUNT + 4;
2361pub const ZPC_HAT: i32 = ZPC_SEG_COUNT + 5;
2362pub const ZPC_HASH: i32 = ZPC_SEG_COUNT + 6;
2363pub const ZPC_BNULLKEEP: i32 = ZPC_SEG_COUNT + 7;
2364pub const ZPC_NO_KSH_GLOB: i32 = ZPC_SEG_COUNT + 8;
2365pub const ZPC_KSH_QUEST: i32 = ZPC_NO_KSH_GLOB;
2366pub const ZPC_KSH_STAR: i32 = ZPC_NO_KSH_GLOB + 1;
2367pub const ZPC_KSH_PLUS: i32 = ZPC_NO_KSH_GLOB + 2;
2368pub const ZPC_KSH_BANG: i32 = ZPC_NO_KSH_GLOB + 3;
2369pub const ZPC_KSH_BANG2: i32 = ZPC_NO_KSH_GLOB + 4;
2370pub const ZPC_KSH_AT: i32 = ZPC_NO_KSH_GLOB + 5;
2371pub const ZPC_COUNT: i32 = ZPC_NO_KSH_GLOB + 6;
2372
2373// =============================================================================
2374// 25. PP_* (zsh.h:1707-1735) + GF_* + ZMB_*.
2375// =============================================================================
2376
2377pub const PP_FIRST: i32 = 1;
2378pub const PP_ALPHA: i32 = 1;
2379pub const PP_ALNUM: i32 = 2;
2380pub const PP_ASCII: i32 = 3;
2381pub const PP_BLANK: i32 = 4;
2382pub const PP_CNTRL: i32 = 5;
2383pub const PP_DIGIT: i32 = 6;
2384pub const PP_GRAPH: i32 = 7;
2385pub const PP_LOWER: i32 = 8;
2386pub const PP_PRINT: i32 = 9;
2387pub const PP_PUNCT: i32 = 10;
2388pub const PP_SPACE: i32 = 11;
2389pub const PP_UPPER: i32 = 12;
2390pub const PP_XDIGIT: i32 = 13;
2391pub const PP_IDENT: i32 = 14;
2392pub const PP_IFS: i32 = 15;
2393pub const PP_IFSSPACE: i32 = 16;
2394pub const PP_WORD: i32 = 17;
2395pub const PP_INCOMPLETE: i32 = 18;
2396pub const PP_INVALID: i32 = 19;
2397pub const PP_LAST: i32 = 19;
2398pub const PP_UNKWN: i32 = 20;
2399pub const PP_RANGE: i32 = 21;
2400
2401pub const GF_LCMATCHUC: i32 = 0x0100;
2402pub const GF_IGNCASE: i32 = 0x0200;
2403pub const GF_BACKREF: i32 = 0x0400;
2404pub const GF_MATCHREF: i32 = 0x0800;
2405pub const GF_MULTIBYTE: i32 = 0x1000;
2406
2407pub const ZMB_VALID: i32 = 0;
2408pub const ZMB_INCOMPLETE: i32 = 1;
2409pub const ZMB_INVALID: i32 = 2;
2410
2411// =============================================================================
2412// 26. Param type flags (zsh.h:1878-1949).
2413// =============================================================================
2414
2415pub const PM_SCALAR: u32 = 0;
2416pub const PM_ARRAY: u32 = 1 << 0;
2417pub const PM_INTEGER: u32 = 1 << 1;
2418pub const PM_EFLOAT: u32 = 1 << 2;
2419pub const PM_FFLOAT: u32 = 1 << 3;
2420pub const PM_HASHED: u32 = 1 << 4;
2421pub const PM_LEFT: u32 = 1 << 5;
2422pub const PM_RIGHT_B: u32 = 1 << 6;
2423pub const PM_RIGHT_Z: u32 = 1 << 7;
2424pub const PM_LOWER: u32 = 1 << 8;
2425pub const PM_UPPER: u32 = 1 << 9;
2426pub const PM_UNDEFINED: u32 = 1 << 9;
2427pub const PM_READONLY: u32 = 1 << 10;
2428pub const PM_TAGGED: u32 = 1 << 11;
2429pub const PM_EXPORTED: u32 = 1 << 12;
2430pub const PM_ABSPATH_USED: u32 = 1 << 12;
2431pub const PM_UNIQUE: u32 = 1 << 13;
2432pub const PM_UNALIASED: u32 = 1 << 13;
2433pub const PM_HIDE: u32 = 1 << 14;
2434pub const PM_CUR_FPATH: u32 = 1 << 14;
2435pub const PM_HIDEVAL: u32 = 1 << 15;
2436pub const PM_WARNNESTED: u32 = 1 << 15;
2437pub const PM_TIED: u32 = 1 << 16;
2438pub const PM_TAGGED_LOCAL: u32 = 1 << 16;
2439pub const PM_DONTIMPORT_SUID: u32 = 1 << 17;
2440pub const PM_LOADDIR: u32 = 1 << 17;
2441pub const PM_SINGLE: u32 = 1 << 18;
2442pub const PM_ANONYMOUS: u32 = 1 << 18;
2443pub const PM_LOCAL: u32 = 1 << 19;
2444pub const PM_KSHSTORED: u32 = 1 << 19;
2445pub const PM_SPECIAL: u32 = 1 << 20;
2446pub const PM_ZSHSTORED: u32 = 1 << 20;
2447pub const PM_RO_BY_DESIGN: u32 = 1 << 21;
2448pub const PM_READONLY_SPECIAL: u32 = PM_SPECIAL | PM_READONLY | PM_RO_BY_DESIGN;
2449pub const PM_DONTIMPORT: u32 = 1 << 22;
2450pub const PM_DECLARED: u32 = 1 << 22;
2451pub const PM_RESTRICTED: u32 = 1 << 23;
2452pub const PM_UNSET: u32 = 1 << 24;
2453pub const PM_DEFAULTED: u32 = PM_DECLARED | PM_UNSET;
2454pub const PM_REMOVABLE: u32 = 1 << 25;
2455pub const PM_AUTOLOAD: u32 = 1 << 26;
2456pub const PM_NORESTORE: u32 = 1 << 27;
2457pub const PM_AUTOALL: u32 = 1 << 27;
2458pub const PM_HASHELEM: u32 = 1 << 28;
2459pub const PM_NAMEDDIR: u32 = 1 << 29;
2460pub const PM_NAMEREF: u32 = 1 << 30;
2461
2462#[inline]
2463#[allow(non_snake_case)]
2464pub const fn PM_TYPE(x: u32) -> u32 {
2465    x & (PM_SCALAR | PM_INTEGER | PM_EFLOAT | PM_FFLOAT | PM_ARRAY | PM_HASHED | PM_NAMEREF)
2466}
2467
2468pub const TYPESET_OPTSTR: &str = "aiEFALRZlurtxUhHT"; // c:1947
2469pub const TYPESET_OPTNUM: &str = "LRZiEF"; // c:1950
2470
2471// =============================================================================
2472// 27. SCANPM_* (zsh.h:1953-1973).
2473// =============================================================================
2474
2475pub const SCANPM_WANTVALS: u32 = 1 << 0;
2476pub const SCANPM_WANTKEYS: u32 = 1 << 1;
2477pub const SCANPM_WANTINDEX: u32 = 1 << 2;
2478pub const SCANPM_MATCHKEY: u32 = 1 << 3;
2479pub const SCANPM_MATCHVAL: u32 = 1 << 4;
2480pub const SCANPM_MATCHMANY: u32 = 1 << 5;
2481pub const SCANPM_ASSIGNING: u32 = 1 << 6;
2482pub const SCANPM_KEYMATCH: u32 = 1 << 7;
2483pub const SCANPM_DQUOTED: u32 = 1 << 8;
2484pub const SCANPM_ARRONLY: u32 = 1 << 9;
2485pub const SCANPM_CHECKING: u32 = 1 << 10;
2486pub const SCANPM_NOEXEC: u32 = 1 << 11;
2487pub const SCANPM_NONAMESPC: u32 = 1 << 12;
2488pub const SCANPM_NONAMEREF: u32 = 1 << 13;
2489pub const SCANPM_ISVAR_AT: u32 = 1 << 14;
2490
2491// =============================================================================
2492// 28. SUB_* substitution flags (zsh.h:1981-1996).
2493// =============================================================================
2494
2495pub const SUB_END: i32 = 0x0001;
2496pub const SUB_LONG: i32 = 0x0002;
2497pub const SUB_SUBSTR: i32 = 0x0004;
2498pub const SUB_MATCH: i32 = 0x0008;
2499pub const SUB_REST: i32 = 0x0010;
2500pub const SUB_BIND: i32 = 0x0020;
2501pub const SUB_EIND: i32 = 0x0040;
2502pub const SUB_LEN: i32 = 0x0080;
2503pub const SUB_ALL: i32 = 0x0100;
2504pub const SUB_GLOBAL: i32 = 0x0200;
2505pub const SUB_DOSUBST: i32 = 0x0400;
2506pub const SUB_RETFAIL: i32 = 0x0800;
2507pub const SUB_START: i32 = 0x1000;
2508pub const SUB_LIST: i32 = 0x2000;
2509pub const SUB_EGLOB: i32 = 0x4000;
2510
2511// =============================================================================
2512// 29. ZSHTOK_* + PREFORK_* + MULTSUB_* (zsh.h:2014-2065).
2513// =============================================================================
2514
2515pub const ZSHTOK_SUBST: i32 = 0x0001;
2516pub const ZSHTOK_SHGLOB: i32 = 0x0002;
2517
2518pub const PREFORK_TYPESET: i32 = 0x01;
2519pub const PREFORK_ASSIGN: i32 = 0x02;
2520pub const PREFORK_SINGLE: i32 = 0x04;
2521pub const PREFORK_SPLIT: i32 = 0x08;
2522pub const PREFORK_SHWORDSPLIT: i32 = 0x10;
2523pub const PREFORK_NOSHWORDSPLIT: i32 = 0x20;
2524pub const PREFORK_SUBEXP: i32 = 0x40;
2525pub const PREFORK_KEY_VALUE: i32 = 0x80;
2526pub const PREFORK_NO_UNTOK: i32 = 0x100;
2527
2528pub const MULTSUB_WS_AT_START: i32 = 1;
2529pub const MULTSUB_WS_AT_END: i32 = 2;
2530pub const MULTSUB_PARAM_NAME: i32 = 4;
2531
2532// =============================================================================
2533// 30. ASSPM_* (zsh.h:2130-2145).
2534// =============================================================================
2535
2536pub const ASSPM_AUGMENT: i32 = 1 << 0;
2537pub const ASSPM_WARN_CREATE: i32 = 1 << 1;
2538pub const ASSPM_WARN_NESTED: i32 = 1 << 2;
2539pub const ASSPM_WARN: i32 = ASSPM_WARN_CREATE | ASSPM_WARN_NESTED;
2540pub const ASSPM_ENV_IMPORT: i32 = 1 << 3;
2541pub const ASSPM_KEY_VALUE: i32 = 1 << 4;
2542
2543// =============================================================================
2544// 31. ND_* + PRINT_* + loop_return + source_return + noerrexit_bits.
2545// =============================================================================
2546
2547pub const ND_USERNAME: i32 = 1 << 1; // c:2157
2548pub const ND_NOABBREV: i32 = 1 << 2; // c:2158
2549
2550pub const PRINT_NAMEONLY: i32 = 1 << 0; // c:2179
2551pub const PRINT_TYPE: i32 = 1 << 1;
2552pub const PRINT_LIST: i32 = 1 << 2;
2553pub const PRINT_KV_PAIR: i32 = 1 << 3;
2554pub const PRINT_INCLUDEVALUE: i32 = 1 << 4;
2555pub const PRINT_TYPESET: i32 = 1 << 5;
2556pub const PRINT_LINE: i32 = 1 << 6;
2557pub const PRINT_POSIX_EXPORT: i32 = 1 << 7;
2558pub const PRINT_POSIX_READONLY: i32 = 1 << 8;
2559pub const PRINT_WITH_NAMESPACE: i32 = 1 << 9;
2560
2561pub const PRINT_WHENCE_CSH: i32 = 1 << 7; // c:2191
2562pub const PRINT_WHENCE_VERBOSE: i32 = 1 << 8;
2563pub const PRINT_WHENCE_SIMPLE: i32 = 1 << 9;
2564pub const PRINT_WHENCE_FUNCDEF: i32 = 1 << 10;
2565pub const PRINT_WHENCE_WORD: i32 = 1 << 11;
2566
2567pub const LOOP_OK: i32 = 0; // c:2199
2568pub const LOOP_EMPTY: i32 = 1;
2569pub const LOOP_ERROR: i32 = 2;
2570
2571pub const SOURCE_OK: i32 = 0; // c:2210
2572pub const SOURCE_NOT_FOUND: i32 = 1;
2573pub const SOURCE_ERROR: i32 = 2;
2574
2575pub const NOERREXIT_EXIT: i32 = 1; // c:2219
2576pub const NOERREXIT_RETURN: i32 = 2;
2577pub const NOERREXIT_SIGNAL: i32 = 8;
2578
2579// =============================================================================
2580// 32. History flags + GETHIST_* + HISTFLAG_* + HFILE_* + LEXFLAGS_*.
2581// =============================================================================
2582
2583pub const HIST_MAKEUNIQUE: u32 = 0x00000001; // c:2252
2584pub const HIST_OLD: u32 = 0x00000002;
2585pub const HIST_READ: u32 = 0x00000004;
2586pub const HIST_DUP: u32 = 0x00000008;
2587pub const HIST_FOREIGN: u32 = 0x00000010;
2588pub const HIST_TMPSTORE: u32 = 0x00000020;
2589pub const HIST_NOWRITE: u32 = 0x00000040;
2590
2591pub const GETHIST_UPWARD: i32 = -1;
2592pub const GETHIST_DOWNWARD: i32 = 1;
2593pub const GETHIST_EXACT: i32 = 0;
2594
2595pub const HISTFLAG_DONE: i32 = 1; // c:2270
2596pub const HISTFLAG_NOEXEC: i32 = 2;
2597pub const HISTFLAG_RECALL: i32 = 4;
2598pub const HISTFLAG_SETTY: i32 = 8;
2599
2600pub const HFILE_APPEND: u32 = 0x0001;
2601pub const HFILE_SKIPOLD: u32 = 0x0002;
2602pub const HFILE_SKIPDUPS: u32 = 0x0004;
2603pub const HFILE_SKIPFOREIGN: u32 = 0x0008;
2604pub const HFILE_FAST: u32 = 0x0010;
2605pub const HFILE_NO_REWRITE: u32 = 0x0020;
2606pub const HFILE_USE_OPTIONS: u32 = 0x8000;
2607
2608pub const LEXFLAGS_ACTIVE: i32 = 0x0001;
2609pub const LEXFLAGS_ZLE: i32 = 0x0002;
2610pub const LEXFLAGS_COMMENTS_KEEP: i32 = 0x0004;
2611pub const LEXFLAGS_COMMENTS_STRIP: i32 = 0x0008;
2612pub const LEXFLAGS_COMMENTS: i32 = LEXFLAGS_COMMENTS_KEEP | LEXFLAGS_COMMENTS_STRIP;
2613pub const LEXFLAGS_NEWLINE: i32 = 0x0010;
2614
2615// =============================================================================
2616// 33. Completion context (zsh.h:2322-2332).
2617// =============================================================================
2618
2619pub const IN_NOTHING: i32 = 0;
2620pub const IN_CMD: i32 = 1;
2621pub const IN_MATH: i32 = 2;
2622pub const IN_COND: i32 = 3;
2623pub const IN_ENV: i32 = 4;
2624pub const IN_PAR: i32 = 5;
2625
2626// =============================================================================
2627// 34. Emulation flags (zsh.h:2341-2358).
2628// =============================================================================
2629
2630pub const EMULATE_CSH: i32 = 1 << 1; // c:2341
2631pub const EMULATE_KSH: i32 = 1 << 2;
2632pub const EMULATE_SH: i32 = 1 << 3;
2633pub const EMULATE_ZSH: i32 = 1 << 4;
2634pub const EMULATE_FULLY: i32 = 1 << 5;
2635pub const EMULATE_UNUSED: i32 = 1 << 6;
2636
2637// =============================================================================
2638// 35. Option indices (zsh.h:2362-2550).
2639// =============================================================================
2640
2641pub const OPT_INVALID: i32 = 0;
2642pub const ALIASESOPT: i32 = 1;
2643pub const ALIASFUNCDEF: i32 = 2;
2644pub const ALLEXPORT: i32 = 3;
2645pub const ALWAYSLASTPROMPT: i32 = 4;
2646pub const ALWAYSTOEND: i32 = 5;
2647pub const APPENDHISTORY: i32 = 6;
2648pub const AUTOCD: i32 = 7;
2649pub const AUTOCONTINUE: i32 = 8;
2650pub const AUTOLIST: i32 = 9;
2651pub const AUTOMENU: i32 = 10;
2652pub const AUTONAMEDIRS: i32 = 11;
2653pub const AUTOPARAMKEYS: i32 = 12;
2654pub const AUTOPARAMSLASH: i32 = 13;
2655pub const AUTOPUSHD: i32 = 14;
2656pub const AUTOREMOVESLASH: i32 = 15;
2657pub const AUTORESUME: i32 = 16;
2658pub const BADPATTERN: i32 = 17;
2659pub const BANGHIST: i32 = 18;
2660pub const BAREGLOBQUAL: i32 = 19;
2661pub const BASHAUTOLIST: i32 = 20;
2662pub const BASHREMATCH: i32 = 21;
2663pub const BEEP: i32 = 22;
2664pub const BGNICE: i32 = 23;
2665pub const BRACECCL: i32 = 24;
2666pub const BSDECHO: i32 = 25;
2667pub const CASEGLOB: i32 = 26;
2668pub const CASEMATCH: i32 = 27;
2669pub const CASEPATHS: i32 = 28;
2670pub const CBASES: i32 = 29;
2671pub const CDABLEVARS: i32 = 30;
2672pub const CDSILENT: i32 = 31;
2673pub const CHASEDOTS: i32 = 32;
2674pub const CHASELINKS: i32 = 33;
2675pub const CHECKJOBS: i32 = 34;
2676pub const CHECKRUNNINGJOBS: i32 = 35;
2677pub const CLOBBER: i32 = 36;
2678pub const CLOBBEREMPTY: i32 = 37;
2679pub const APPENDCREATE: i32 = 38;
2680pub const COMBININGCHARS: i32 = 39;
2681pub const COMPLETEALIASES: i32 = 40;
2682pub const COMPLETEINWORD: i32 = 41;
2683pub const CORRECT: i32 = 42;
2684pub const CORRECTALL: i32 = 43;
2685pub const CONTINUEONERROR: i32 = 44;
2686pub const CPRECEDENCES: i32 = 45;
2687pub const CSHJUNKIEHISTORY: i32 = 46;
2688pub const CSHJUNKIELOOPS: i32 = 47;
2689pub const CSHJUNKIEQUOTES: i32 = 48;
2690pub const CSHNULLCMD: i32 = 49;
2691pub const CSHNULLGLOB: i32 = 50;
2692pub const DEBUGBEFORECMD: i32 = 51;
2693pub const EMACSMODE: i32 = 52;
2694pub const EQUALSOPT: i32 = 53; // C name "EQUALS" collides with our token const
2695pub const ERREXIT: i32 = 54;
2696pub const ERRRETURN: i32 = 55;
2697pub const EXECOPT: i32 = 56;
2698pub const EXTENDEDGLOB: i32 = 57;
2699pub const EXTENDEDHISTORY: i32 = 58;
2700pub const EVALLINENO: i32 = 59;
2701pub const FLOWCONTROL: i32 = 60;
2702pub const FORCEFLOAT: i32 = 61;
2703pub const FUNCTIONARGZERO: i32 = 62;
2704pub const GLOBOPT: i32 = 63;
2705pub const GLOBALEXPORT: i32 = 64;
2706pub const GLOBALRCS: i32 = 65;
2707pub const GLOBASSIGN: i32 = 66;
2708pub const GLOBCOMPLETE: i32 = 67;
2709pub const GLOBDOTS: i32 = 68;
2710pub const GLOBSTARSHORT: i32 = 69;
2711pub const GLOBSUBST: i32 = 70;
2712pub const HASHCMDS: i32 = 71;
2713pub const HASHDIRS: i32 = 72;
2714pub const HASHEXECUTABLESONLY: i32 = 73;
2715pub const HASHLISTALL: i32 = 74;
2716pub const HISTALLOWCLOBBER: i32 = 75;
2717pub const HISTBEEP: i32 = 76;
2718pub const HISTEXPIREDUPSFIRST: i32 = 77;
2719pub const HISTFCNTLLOCK: i32 = 78;
2720pub const HISTFINDNODUPS: i32 = 79;
2721pub const HISTIGNOREALLDUPS: i32 = 80;
2722pub const HISTIGNOREDUPS: i32 = 81;
2723pub const HISTIGNORESPACE: i32 = 82;
2724pub const HISTLEXWORDS: i32 = 83;
2725pub const HISTNOFUNCTIONS: i32 = 84;
2726pub const HISTNOSTORE: i32 = 85;
2727pub const HISTREDUCEBLANKS: i32 = 86;
2728pub const HISTSAVEBYCOPY: i32 = 87;
2729pub const HISTSAVENODUPS: i32 = 88;
2730pub const HISTSUBSTPATTERN: i32 = 89;
2731pub const HISTVERIFY: i32 = 90;
2732pub const HUP: i32 = 91;
2733pub const IGNOREBRACES: i32 = 92;
2734pub const IGNORECLOSEBRACES: i32 = 93;
2735pub const IGNOREEOF: i32 = 94;
2736pub const INCAPPENDHISTORY: i32 = 95;
2737pub const INCAPPENDHISTORYTIME: i32 = 96;
2738pub const INTERACTIVE: i32 = 97;
2739pub const INTERACTIVECOMMENTS: i32 = 98;
2740pub const KSHARRAYS: i32 = 99;
2741pub const KSHAUTOLOAD: i32 = 100;
2742pub const KSHGLOB: i32 = 101;
2743pub const KSHOPTIONPRINT: i32 = 102;
2744pub const KSHTYPESET: i32 = 103;
2745pub const KSHZEROSUBSCRIPT: i32 = 104;
2746pub const LISTAMBIGUOUS: i32 = 105;
2747pub const LISTBEEP: i32 = 106;
2748pub const LISTPACKED: i32 = 107;
2749pub const LISTROWSFIRST: i32 = 108;
2750pub const LISTTYPES: i32 = 109;
2751pub const LOCALLOOPS: i32 = 110;
2752pub const LOCALOPTIONS: i32 = 111;
2753pub const LOCALPATTERNS: i32 = 112;
2754pub const LOCALTRAPS: i32 = 113;
2755pub const LOGINSHELL: i32 = 114;
2756pub const LONGLISTJOBS: i32 = 115;
2757pub const MAGICEQUALSUBST: i32 = 116;
2758pub const MAILWARNING: i32 = 117;
2759pub const MARKDIRS: i32 = 118;
2760pub const MENUCOMPLETE: i32 = 119;
2761pub const MONITOR: i32 = 120;
2762pub const MULTIBYTE: i32 = 121;
2763pub const MULTIFUNCDEF: i32 = 122;
2764pub const MULTIOS: i32 = 123;
2765pub const NOMATCH: i32 = 124;
2766pub const NOTIFY: i32 = 125;
2767pub const NULLGLOB: i32 = 126;
2768pub const NUMERICGLOBSORT: i32 = 127;
2769pub const OCTALZEROES: i32 = 128;
2770pub const OVERSTRIKE: i32 = 129;
2771pub const PATHDIRS: i32 = 130;
2772pub const PATHSCRIPT: i32 = 131;
2773pub const PIPEFAIL: i32 = 132;
2774pub const POSIXALIASES: i32 = 133;
2775pub const POSIXARGZERO: i32 = 134;
2776pub const POSIXBUILTINS: i32 = 135;
2777pub const POSIXCD: i32 = 136;
2778pub const POSIXIDENTIFIERS: i32 = 137;
2779pub const POSIXJOBS: i32 = 138;
2780pub const POSIXSTRINGS: i32 = 139;
2781pub const POSIXTRAPS: i32 = 140;
2782pub const PRINTEIGHTBIT: i32 = 141;
2783pub const PRINTEXITVALUE: i32 = 142;
2784pub const PRIVILEGED: i32 = 143;
2785pub const PROMPTBANG: i32 = 144;
2786pub const PROMPTCR: i32 = 145;
2787pub const PROMPTPERCENT: i32 = 146;
2788pub const PROMPTSP: i32 = 147;
2789pub const PROMPTSUBST: i32 = 148;
2790pub const PUSHDIGNOREDUPS: i32 = 149;
2791pub const PUSHDMINUS: i32 = 150;
2792pub const PUSHDSILENT: i32 = 151;
2793pub const PUSHDTOHOME: i32 = 152;
2794pub const RCEXPANDPARAM: i32 = 153;
2795pub const RCQUOTES: i32 = 154;
2796pub const RCS: i32 = 155;
2797pub const RECEXACT: i32 = 156;
2798pub const REMATCHPCRE: i32 = 157;
2799pub const RESTRICTED: i32 = 158;
2800pub const RMSTARSILENT: i32 = 159;
2801pub const RMSTARWAIT: i32 = 160;
2802pub const SHAREHISTORY: i32 = 161;
2803pub const SHFILEEXPANSION: i32 = 162;
2804pub const SHGLOB: i32 = 163;
2805pub const SHINSTDIN: i32 = 164;
2806pub const SHNULLCMD: i32 = 165;
2807pub const SHOPTIONLETTERS: i32 = 166;
2808pub const SHORTLOOPS: i32 = 167;
2809pub const SHORTREPEAT: i32 = 168;
2810pub const SHWORDSPLIT: i32 = 169;
2811pub const SINGLECOMMAND: i32 = 170;
2812pub const SINGLELINEZLE: i32 = 171;
2813pub const SOURCETRACE: i32 = 172;
2814pub const SUNKEYBOARDHACK: i32 = 173;
2815pub const TRANSIENTRPROMPT: i32 = 174;
2816pub const TRAPSASYNC: i32 = 175;
2817pub const TYPESETSILENT: i32 = 176;
2818pub const TYPESETTOUNSET: i32 = 177;
2819pub const UNSET: i32 = 178;
2820pub const VERBOSE: i32 = 179;
2821pub const VIMODE: i32 = 180;
2822pub const WARNCREATEGLOBAL: i32 = 181;
2823pub const WARNNESTEDVAR: i32 = 182;
2824pub const XTRACE: i32 = 183;
2825pub const USEZLE: i32 = 184;
2826pub const DVORAK: i32 = 185;
2827pub const OPT_SIZE: i32 = 186;
2828
2829pub type OptIndex = u8; // c:2556
2830
2831// #define isset(X) (opts[X])                                               // c:2559
2832/// Port of `isset(X)` macro from `Src/zsh.h:2559`.
2833/// Returns true if option is set.
2834#[inline]
2835pub fn isset(opt: i32) -> bool {
2836    crate::ported::options::opt_state_get(&opt_name(opt)).unwrap_or(false)
2837}
2838
2839// #define unset(X) (!opts[X])                                              // c:2560
2840/// Port of `unset(X)` macro from `Src/zsh.h:2560`.
2841/// Returns true if option is NOT set.
2842#[inline]
2843pub fn unset(opt: i32) -> bool {
2844    !isset(opt)
2845}
2846
2847// #define interact (isset(INTERACTIVE))                                    // c:2562
2848/// Port of `interact` macro from `Src/zsh.h:2562`.
2849#[inline]
2850pub fn interact() -> bool {
2851    isset(INTERACTIVE)
2852}
2853
2854// #define jobbing  (isset(MONITOR))                                        // c:2563
2855/// Port of `jobbing` macro from `Src/zsh.h:2563`.
2856#[inline]
2857pub fn jobbing() -> bool {
2858    isset(MONITOR)
2859}
2860
2861// #define islogin  (isset(LOGINSHELL))                                     // c:2564
2862/// Port of `islogin` macro from `Src/zsh.h:2564`.
2863#[inline]
2864pub fn islogin() -> bool {
2865    isset(LOGINSHELL)
2866}
2867
2868/// Helper: convert option constant to its name for lookup.
2869pub fn opt_name(opt: i32) -> &'static str {
2870    match opt {
2871        x if x == ALLEXPORT => "allexport",
2872        x if x == ALWAYSLASTPROMPT => "alwayslastprompt",
2873        x if x == ALWAYSTOEND => "alwaystoend",
2874        x if x == APPENDHISTORY => "appendhistory",
2875        x if x == AUTOCD => "autocd",
2876        x if x == AUTOCONTINUE => "autocontinue",
2877        x if x == AUTOLIST => "autolist",
2878        x if x == AUTOMENU => "automenu",
2879        x if x == AUTONAMEDIRS => "autonamedirs",
2880        x if x == AUTOPARAMKEYS => "autoparamkeys",
2881        x if x == AUTOPARAMSLASH => "autoparamslash",
2882        x if x == AUTOPUSHD => "autopushd",
2883        x if x == AUTOREMOVESLASH => "autoremoveslash",
2884        x if x == AUTORESUME => "autoresume",
2885        x if x == BADPATTERN => "badpattern",
2886        x if x == BANGHIST => "banghist",
2887        x if x == BAREGLOBQUAL => "bareglobqual",
2888        x if x == BASHAUTOLIST => "bashautolist",
2889        x if x == BASHREMATCH => "bashrematch",
2890        x if x == BEEP => "beep",
2891        x if x == BGNICE => "bgnice",
2892        x if x == BRACECCL => "braceccl",
2893        x if x == BSDECHO => "bsdecho",
2894        x if x == CASEGLOB => "caseglob",
2895        x if x == CASEMATCH => "casematch",
2896        x if x == CBASES => "cbases",
2897        x if x == CDABLEVARS => "cdablevars",
2898        x if x == CDSILENT => "cdsilent",
2899        x if x == CHASEDOTS => "chasedots",
2900        x if x == CHASELINKS => "chaselinks",
2901        x if x == CHECKJOBS => "checkjobs",
2902        x if x == CHECKRUNNINGJOBS => "checkrunningjobs",
2903        x if x == CLOBBER => "clobber",
2904        x if x == COMBININGCHARS => "combiningchars",
2905        x if x == COMPLETEALIASES => "completealiases",
2906        x if x == COMPLETEINWORD => "completeinword",
2907        x if x == CORRECT => "correct",
2908        x if x == CORRECTALL => "correctall",
2909        x if x == CPRECEDENCES => "cprecedences",
2910        x if x == CSHJUNKIEHISTORY => "cshjunkiehistory",
2911        x if x == CSHJUNKIELOOPS => "cshjunkieloops",
2912        x if x == CSHJUNKIEQUOTES => "cshjunkiequotes",
2913        x if x == CSHNULLCMD => "cshnullcmd",
2914        x if x == CSHNULLGLOB => "cshnullglob",
2915        x if x == DEBUGBEFORECMD => "debugbeforecmd",
2916        x if x == EMACSMODE => "emacs",
2917        x if x == EQUALSOPT => "equals",
2918        x if x == ERREXIT => "errexit",
2919        x if x == ERRRETURN => "errreturn",
2920        x if x == EXECOPT => "exec",
2921        x if x == EXTENDEDGLOB => "extendedglob",
2922        x if x == EXTENDEDHISTORY => "extendedhistory",
2923        x if x == EVALLINENO => "evallineno",
2924        x if x == FLOWCONTROL => "flowcontrol",
2925        x if x == FORCEFLOAT => "forcefloat",
2926        x if x == FUNCTIONARGZERO => "functionargzero",
2927        x if x == GLOBOPT => "glob",
2928        x if x == GLOBALEXPORT => "globalexport",
2929        x if x == GLOBALRCS => "globalrcs",
2930        x if x == GLOBASSIGN => "globassign",
2931        x if x == GLOBCOMPLETE => "globcomplete",
2932        x if x == GLOBDOTS => "globdots",
2933        x if x == GLOBSTARSHORT => "globstarshort",
2934        x if x == GLOBSUBST => "globsubst",
2935        x if x == HASHCMDS => "hashcmds",
2936        x if x == HASHDIRS => "hashdirs",
2937        x if x == HASHEXECUTABLESONLY => "hashexecutablesonly",
2938        x if x == HASHLISTALL => "hashlistall",
2939        x if x == HISTALLOWCLOBBER => "histallowclobber",
2940        x if x == HISTBEEP => "histbeep",
2941        x if x == HISTEXPIREDUPSFIRST => "histexpiredupsfirst",
2942        x if x == HISTFCNTLLOCK => "histfcntllock",
2943        x if x == HISTFINDNODUPS => "histfindnodups",
2944        x if x == HISTIGNOREALLDUPS => "histignorealldups",
2945        x if x == HISTIGNOREDUPS => "histignoredups",
2946        x if x == HISTIGNORESPACE => "histignorespace",
2947        x if x == HISTLEXWORDS => "histlexwords",
2948        x if x == HISTNOFUNCTIONS => "histnofunctions",
2949        x if x == HISTNOSTORE => "histnostore",
2950        x if x == HISTREDUCEBLANKS => "histreduceblanks",
2951        x if x == HISTSAVEBYCOPY => "histsavebycopy",
2952        x if x == HISTSAVENODUPS => "histsavenodups",
2953        x if x == HISTSUBSTPATTERN => "histsubstpattern",
2954        x if x == HISTVERIFY => "histverify",
2955        x if x == HUP => "hup",
2956        x if x == IGNOREBRACES => "ignorebraces",
2957        x if x == IGNORECLOSEBRACES => "ignoreclosebraces",
2958        x if x == IGNOREEOF => "ignoreeof",
2959        x if x == INCAPPENDHISTORY => "incappendhistory",
2960        x if x == INCAPPENDHISTORYTIME => "incappendhistorytime",
2961        x if x == INTERACTIVE => "interactive",
2962        x if x == INTERACTIVECOMMENTS => "interactivecomments",
2963        x if x == KSHARRAYS => "ksharrays",
2964        x if x == KSHAUTOLOAD => "kshautoload",
2965        x if x == KSHGLOB => "kshglob",
2966        x if x == KSHOPTIONPRINT => "kshoptionprint",
2967        x if x == KSHTYPESET => "kshtypeset",
2968        x if x == KSHZEROSUBSCRIPT => "kshzerosubscript",
2969        x if x == LISTAMBIGUOUS => "listambiguous",
2970        x if x == LISTBEEP => "listbeep",
2971        x if x == LISTPACKED => "listpacked",
2972        x if x == LISTROWSFIRST => "listrowsfirst",
2973        x if x == LISTTYPES => "listtypes",
2974        x if x == LOCALOPTIONS => "localoptions",
2975        x if x == LOCALLOOPS => "localloops",
2976        x if x == LOCALPATTERNS => "localpatterns",
2977        x if x == LOCALTRAPS => "localtraps",
2978        x if x == LOGINSHELL => "loginshell",
2979        x if x == LONGLISTJOBS => "longlistjobs",
2980        x if x == MAGICEQUALSUBST => "magicequalsubst",
2981        x if x == MAILWARNING => "mailwarning",
2982        x if x == MARKDIRS => "markdirs",
2983        x if x == MENUCOMPLETE => "menucomplete",
2984        x if x == MONITOR => "monitor",
2985        x if x == MULTIBYTE => "multibyte",
2986        x if x == MULTIFUNCDEF => "multifuncdef",
2987        x if x == MULTIOS => "multios",
2988        x if x == NOMATCH => "nomatch",
2989        x if x == NOTIFY => "notify",
2990        x if x == NULLGLOB => "nullglob",
2991        x if x == NUMERICGLOBSORT => "numericglobsort",
2992        x if x == OCTALZEROES => "octalzeroes",
2993        x if x == OVERSTRIKE => "overstrike",
2994        x if x == PATHDIRS => "pathdirs",
2995        x if x == PATHSCRIPT => "pathscript",
2996        x if x == PIPEFAIL => "pipefail",
2997        x if x == POSIXALIASES => "posixaliases",
2998        x if x == POSIXARGZERO => "posixargzero",
2999        x if x == POSIXBUILTINS => "posixbuiltins",
3000        x if x == POSIXCD => "posixcd",
3001        x if x == POSIXIDENTIFIERS => "posixidentifiers",
3002        x if x == POSIXJOBS => "posixjobs",
3003        x if x == POSIXSTRINGS => "posixstrings",
3004        x if x == POSIXTRAPS => "posixtraps",
3005        x if x == PRINTEIGHTBIT => "printeightbit",
3006        x if x == PRINTEXITVALUE => "printexitvalue",
3007        x if x == PRIVILEGED => "privileged",
3008        x if x == PROMPTBANG => "promptbang",
3009        x if x == PROMPTCR => "promptcr",
3010        x if x == PROMPTPERCENT => "promptpercent",
3011        x if x == PROMPTSP => "promptsp",
3012        x if x == PROMPTSUBST => "promptsubst",
3013        x if x == PUSHDIGNOREDUPS => "pushdignoredups",
3014        x if x == PUSHDMINUS => "pushdminus",
3015        x if x == PUSHDSILENT => "pushdsilent",
3016        x if x == PUSHDTOHOME => "pushdtohome",
3017        x if x == RCEXPANDPARAM => "rcexpandparam",
3018        x if x == RCQUOTES => "rcquotes",
3019        x if x == RCS => "rcs",
3020        x if x == RECEXACT => "recexact",
3021        x if x == REMATCHPCRE => "rematchpcre",
3022        x if x == RESTRICTED => "restricted",
3023        x if x == RMSTARSILENT => "rmstarsilent",
3024        x if x == RMSTARWAIT => "rmstarwait",
3025        x if x == SHAREHISTORY => "sharehistory",
3026        x if x == SHFILEEXPANSION => "shfileexpansion",
3027        x if x == SHGLOB => "shglob",
3028        x if x == SHINSTDIN => "shinstdin",
3029        x if x == SHNULLCMD => "shnullcmd",
3030        x if x == SHOPTIONLETTERS => "shoptionletters",
3031        x if x == SHORTLOOPS => "shortloops",
3032        x if x == SHORTREPEAT => "shortrepeat",
3033        x if x == SHWORDSPLIT => "shwordsplit",
3034        x if x == SINGLECOMMAND => "singlecommand",
3035        x if x == SINGLELINEZLE => "singlelinezle",
3036        x if x == SOURCETRACE => "sourcetrace",
3037        x if x == SUNKEYBOARDHACK => "sunkeyboardhack",
3038        x if x == TRANSIENTRPROMPT => "transientrprompt",
3039        x if x == TRAPSASYNC => "trapsasync",
3040        x if x == TYPESETSILENT => "typesetsilent",
3041        x if x == UNSET => "unset",
3042        x if x == VERBOSE => "verbose",
3043        x if x == ALIASESOPT => "aliases",
3044        x if x == WARNCREATEGLOBAL => "warncreateglobal",
3045        x if x == WARNNESTEDVAR => "warnnestedvar",
3046        x if x == XTRACE => "xtrace",
3047        x if x == USEZLE => "zle",
3048        x if x == DVORAK => "dvorak",
3049        _ => "",
3050    }
3051}
3052
3053// =============================================================================
3054// 36. Terminal control (zsh.h:2633-2680).
3055// =============================================================================
3056
3057pub const TERM_BAD: i32 = 0x01;
3058pub const TERM_UNKNOWN: i32 = 0x02;
3059pub const TERM_NOUP: i32 = 0x04;
3060pub const TERM_SHORT: i32 = 0x08;
3061pub const TERM_NARROW: i32 = 0x10;
3062
3063pub const TCCLEARSCREEN: i32 = 0;
3064pub const TCLEFT: i32 = 1;
3065pub const TCMULTLEFT: i32 = 2;
3066pub const TCRIGHT: i32 = 3;
3067pub const TCMULTRIGHT: i32 = 4;
3068pub const TCUP: i32 = 5;
3069pub const TCMULTUP: i32 = 6;
3070pub const TCDOWN: i32 = 7;
3071pub const TCMULTDOWN: i32 = 8;
3072pub const TCDEL: i32 = 9;
3073pub const TCMULTDEL: i32 = 10;
3074pub const TCINS: i32 = 11;
3075pub const TCMULTINS: i32 = 12;
3076pub const TCCLEAREOD: i32 = 13;
3077pub const TCCLEAREOL: i32 = 14;
3078pub const TCINSLINE: i32 = 15;
3079pub const TCDELLINE: i32 = 16;
3080pub const TCNEXTTAB: i32 = 17;
3081pub const TCBOLDFACEBEG: i32 = 18;
3082pub const TCFAINTBEG: i32 = 19;
3083pub const TCSTANDOUTBEG: i32 = 20;
3084pub const TCUNDERLINEBEG: i32 = 21;
3085pub const TCITALICSBEG: i32 = 22;
3086pub const TCALLATTRSOFF: i32 = 23;
3087pub const TCSTANDOUTEND: i32 = 24;
3088pub const TCUNDERLINEEND: i32 = 25;
3089pub const TCITALICSEND: i32 = 26;
3090pub const TCHORIZPOS: i32 = 27;
3091pub const TCUPCURSOR: i32 = 28;
3092pub const TCDOWNCURSOR: i32 = 29;
3093pub const TCLEFTCURSOR: i32 = 30;
3094pub const TCRIGHTCURSOR: i32 = 31;
3095pub const TCSAVECURSOR: i32 = 32;
3096pub const TCRESTRCURSOR: i32 = 33;
3097pub const TCBACKSPACE: i32 = 34;
3098pub const TCFGCOLOUR: i32 = 35;
3099pub const TCBGCOLOUR: i32 = 36;
3100pub const TCCURINV: i32 = 37;
3101pub const TCCURVIS: i32 = 38;
3102pub const TC_COUNT: i32 = 39;
3103
3104// =============================================================================
3105// 37. Text attributes (zattr) (zsh.h:2689-2750).
3106// =============================================================================
3107
3108pub type zattr = u64; // c:2689
3109
3110pub const TXTBOLDFACE: zattr = 0x0001;
3111pub const TXTFAINT: zattr = 0x0002;
3112pub const TXTSTANDOUT: zattr = 0x0004;
3113pub const TXTUNDERLINE: zattr = 0x0008;
3114pub const TXTITALIC: zattr = 0x0010;
3115pub const TXTFGCOLOUR: zattr = 0x0020;
3116pub const TXTBGCOLOUR: zattr = 0x0040;
3117
3118pub const TXT_ATTR_ALL: zattr = 0x007F;
3119pub const TXT_MULTIWORD_MASK: zattr = 0x0400;
3120pub const TXT_ERROR: zattr = 0xF00000F000000003;
3121pub const TXT_ATTR_FONT_WEIGHT: zattr = TXTBOLDFACE | TXTFAINT;
3122
3123pub const TXT_ATTR_FG_COL_MASK: zattr = 0x000000FFFFFF0000;
3124pub const TXT_ATTR_FG_COL_SHIFT: u32 = 16;
3125pub const TXT_ATTR_BG_COL_MASK: zattr = 0xFFFFFF0000000000;
3126pub const TXT_ATTR_BG_COL_SHIFT: u32 = 40;
3127
3128pub const TXT_ATTR_FG_24BIT: zattr = 0x4000;
3129pub const TXT_ATTR_BG_24BIT: zattr = 0x8000;
3130
3131pub const TXT_ATTR_FG_MASK: zattr = TXTFGCOLOUR | TXT_ATTR_FG_COL_MASK | TXT_ATTR_FG_24BIT;
3132pub const TXT_ATTR_BG_MASK: zattr = TXTBGCOLOUR | TXT_ATTR_BG_COL_MASK | TXT_ATTR_BG_24BIT;
3133pub const TXT_ATTR_COLOUR_MASK: zattr = TXT_ATTR_FG_MASK | TXT_ATTR_BG_MASK;
3134
3135pub const COL_SEQ_FG: i32 = 0;
3136pub const COL_SEQ_BG: i32 = 1;
3137
3138#[allow(non_camel_case_types)]
3139pub struct color_rgb {
3140    // c:2752
3141    pub red: u32,
3142    pub green: u32,
3143    pub blue: u32,
3144}
3145pub type Color_rgb = Box<color_rgb>;
3146
3147pub const TSC_RAW: i32 = 0x0001; // c:2764
3148pub const TSC_PROMPT: i32 = 0x0002;
3149
3150// =============================================================================
3151// 38. Prompt %_ command stack (zsh.h:2773-2809).
3152// =============================================================================
3153
3154pub const CMDSTACKSZ: usize = 256;
3155pub const CS_FOR: i32 = 0;
3156pub const CS_WHILE: i32 = 1;
3157pub const CS_REPEAT: i32 = 2;
3158pub const CS_SELECT: i32 = 3;
3159pub const CS_UNTIL: i32 = 4;
3160pub const CS_IF: i32 = 5;
3161pub const CS_IFTHEN: i32 = 6;
3162pub const CS_ELSE: i32 = 7;
3163pub const CS_ELIF: i32 = 8;
3164pub const CS_MATH: i32 = 9;
3165pub const CS_COND: i32 = 10;
3166pub const CS_CMDOR: i32 = 11;
3167pub const CS_CMDAND: i32 = 12;
3168pub const CS_PIPE: i32 = 13;
3169pub const CS_ERRPIPE: i32 = 14;
3170pub const CS_FOREACH: i32 = 15;
3171pub const CS_CASE: i32 = 16;
3172pub const CS_FUNCDEF: i32 = 17;
3173pub const CS_SUBSH: i32 = 18;
3174pub const CS_CURSH: i32 = 19;
3175pub const CS_ARRAY: i32 = 20;
3176pub const CS_QUOTE: i32 = 21;
3177pub const CS_DQUOTE: i32 = 22;
3178pub const CS_BQUOTE: i32 = 23;
3179pub const CS_CMDSUBST: i32 = 24;
3180pub const CS_MATHSUBST: i32 = 25;
3181pub const CS_ELIFTHEN: i32 = 26;
3182pub const CS_HEREDOC: i32 = 27;
3183pub const CS_HEREDOCD: i32 = 28;
3184pub const CS_BRACE: i32 = 29;
3185pub const CS_BRACEPAR: i32 = 30;
3186pub const CS_ALWAYS: i32 = 31;
3187pub const CS_COUNT: i32 = 32;
3188
3189// =============================================================================
3190// 39. Heap memory + Heapid (zsh.h:2826-2862).
3191// =============================================================================
3192
3193pub type Heapid = u32; // c:2826
3194
3195pub const HEAPID_PERMANENT: Heapid = u32::MAX; // c:2834
3196
3197pub const HDV_PUSH: i32 = 0x01;
3198pub const HDV_POP: i32 = 0x02;
3199pub const HDV_CREATE: i32 = 0x04;
3200pub const HDV_FREE: i32 = 0x08;
3201pub const HDV_NEW: i32 = 0x10;
3202pub const HDV_OLD: i32 = 0x20;
3203pub const HDV_SWITCH: i32 = 0x40;
3204pub const HDV_ALLOC: i32 = 0x80;
3205
3206// =============================================================================
3207// 40. Signal trap state (zsh.h:2935-2984).
3208// =============================================================================
3209
3210pub const ZSIG_TRAPPED: i32 = 1 << 0;
3211pub const ZSIG_IGNORED: i32 = 1 << 1;
3212pub const ZSIG_FUNC: i32 = 1 << 2;
3213pub const ZSIG_MASK: i32 = ZSIG_TRAPPED | ZSIG_IGNORED | ZSIG_FUNC;
3214pub const ZSIG_ALIAS: i32 = 1 << 3;
3215pub const ZSIG_SHIFT: i32 = 4;
3216
3217pub const TRAP_STATE_INACTIVE: i32 = 0;
3218pub const TRAP_STATE_PRIMED: i32 = 1;
3219pub const TRAP_STATE_FORCE_RETURN: i32 = 2;
3220
3221pub const ERRFLAG_ERROR: i32 = 1;
3222pub const ERRFLAG_INT: i32 = 2;
3223pub const ERRFLAG_HARD: i32 = 4;
3224
3225// =============================================================================
3226// 41. Sorting (zsh.h:2992-3008).
3227// =============================================================================
3228
3229pub const SORTIT_ANYOLDHOW: i32 = 0;
3230pub const SORTIT_IGNORING_CASE: i32 = 1;
3231pub const SORTIT_NUMERICALLY: i32 = 2;
3232pub const SORTIT_NUMERICALLY_SIGNED: i32 = 4;
3233pub const SORTIT_BACKWARDS: i32 = 8;
3234pub const SORTIT_IGNORING_BACKSLASHES: i32 = 16;
3235pub const SORTIT_SOMEHOW: i32 = 32;
3236
3237// =============================================================================
3238// 42. Case modify + Getkey (zsh.h:3122-3197).
3239// =============================================================================
3240
3241pub const CASMOD_NONE: i32 = 0;
3242pub const CASMOD_UPPER: i32 = 1;
3243pub const CASMOD_LOWER: i32 = 2;
3244pub const CASMOD_CAPS: i32 = 3;
3245
3246pub const GETKEY_OCTAL_ESC: i32 = 1 << 0;
3247pub const GETKEY_EMACS: i32 = 1 << 1;
3248pub const GETKEY_CTRL: i32 = 1 << 2;
3249pub const GETKEY_BACKSLASH_C: i32 = 1 << 3;
3250pub const GETKEY_DOLLAR_QUOTE: i32 = 1 << 4;
3251pub const GETKEY_BACKSLASH_MINUS: i32 = 1 << 5;
3252pub const GETKEY_SINGLE_CHAR: i32 = 1 << 6;
3253pub const GETKEY_UPDATE_OFFSET: i32 = 1 << 7;
3254pub const GETKEY_PRINTF_PERCENT: i32 = 1 << 8;
3255
3256pub const GETKEYS_ECHO: i32 = GETKEY_BACKSLASH_C;
3257pub const GETKEYS_PRINTF_FMT: i32 = GETKEY_OCTAL_ESC | GETKEY_BACKSLASH_C | GETKEY_PRINTF_PERCENT;
3258pub const GETKEYS_PRINTF_ARG: i32 = GETKEY_BACKSLASH_C;
3259pub const GETKEYS_PRINT: i32 = GETKEY_OCTAL_ESC | GETKEY_BACKSLASH_C | GETKEY_EMACS;
3260pub const GETKEYS_BINDKEY: i32 = GETKEY_OCTAL_ESC | GETKEY_EMACS | GETKEY_CTRL;
3261pub const GETKEYS_DOLLARS_QUOTE: i32 = GETKEY_OCTAL_ESC | GETKEY_EMACS | GETKEY_DOLLAR_QUOTE;
3262pub const GETKEYS_MATH: i32 = GETKEY_OCTAL_ESC | GETKEY_EMACS | GETKEY_CTRL | GETKEY_SINGLE_CHAR;
3263pub const GETKEYS_SEP: i32 = GETKEY_OCTAL_ESC | GETKEY_EMACS;
3264pub const GETKEYS_SUFFIX: i32 =
3265    GETKEY_OCTAL_ESC | GETKEY_EMACS | GETKEY_CTRL | GETKEY_BACKSLASH_MINUS;
3266
3267// =============================================================================
3268// 43. zle flags (zsh.h:3203-3216).
3269// =============================================================================
3270
3271pub const ZLRF_HISTORY: i32 = 0x01;
3272pub const ZLRF_NOSETTY: i32 = 0x02;
3273pub const ZLRF_IGNOREEOF: i32 = 0x04;
3274
3275pub const ZLCON_LINE_START: i32 = 0;
3276pub const ZLCON_LINE_CONT: i32 = 1;
3277pub const ZLCON_SELECT: i32 = 2;
3278pub const ZLCON_VARED: i32 = 3;
3279
3280pub const ZLE_CMD_GET_LINE: i32 = 0;
3281pub const ZLE_CMD_READ: i32 = 1;
3282pub const ZLE_CMD_ADD_TO_LINE: i32 = 2;
3283pub const ZLE_CMD_TRASH: i32 = 3;
3284pub const ZLE_CMD_RESET_PROMPT: i32 = 4;
3285pub const ZLE_CMD_REFRESH: i32 = 5;
3286pub const ZLE_CMD_SET_KEYMAP: i32 = 6;
3287pub const ZLE_CMD_GET_KEY: i32 = 7;
3288pub const ZLE_CMD_SET_HIST_LINE: i32 = 8;
3289pub const ZLE_CMD_PREEXEC: i32 = 9;
3290pub const ZLE_CMD_POSTEXEC: i32 = 10;
3291pub const ZLE_CMD_CHPWD: i32 = 11;
3292
3293// =============================================================================
3294// 44. zexit + nice format (zsh.h:3252-3268).
3295// =============================================================================
3296
3297pub const ZEXIT_NORMAL: i32 = 0;
3298pub const ZEXIT_SIGNAL: i32 = 1;
3299pub const ZEXIT_DEFERRED: i32 = 2;
3300
3301pub const NICEFLAG_HEAP: i32 = 1;
3302pub const NICEFLAG_QUOTE: i32 = 2;
3303pub const NICEFLAG_NODUP: i32 = 4;
3304
3305// =============================================================================
3306// 45. Multibyte macros (zsh.h:3271-3375).
3307// =============================================================================
3308
3309pub type convchar_t = u32; // c:3276/3357
3310
3311pub const MB_INCOMPLETE: usize = usize::MAX - 1; // c:3313
3312pub const MB_INVALID: usize = usize::MAX; // c:3314
3313pub const MB_CUR_MAX: usize = 6; // c:3324
3314
3315/// Port of `MB_METACHARINIT()` from `Src/zsh.h:3275/3356`. C calls
3316/// `mb_charinit()` to reset multibyte state. Rust char iteration is
3317/// stateless; no-op.
3318#[inline]
3319#[allow(non_snake_case)]
3320pub fn MB_METACHARINIT() {} // c:3275
3321
3322/// Port of `MB_METACHARLEN(str)` from `Src/zsh.h:3278/3359`. Returns
3323/// the byte length of the next metafied character. C: `*str == Meta
3324/// ? 2 : 1` (non-multibyte); `mb_metacharlenconv(str, NULL)`
3325/// (multibyte). Rust returns the same byte length.
3326#[inline]
3327#[allow(non_snake_case)]
3328pub fn MB_METACHARLEN(s: &[u8]) -> usize {
3329    // c:3278/3359
3330    if s.is_empty() {
3331        0
3332    } else if s[0] as char == META {
3333        2
3334    } else {
3335        1
3336    }
3337}
3338
3339/// Port of `MB_METACHARLENCONV(str, cp)` from `Src/zsh.h:3277/3358`.
3340/// Returns byte length + (optionally) the converted char. Rust port
3341/// returns `(byte_len, Option<char>)`.
3342#[inline]
3343#[allow(non_snake_case)]
3344pub fn MB_METACHARLENCONV(s: &[u8]) -> (usize, Option<char>) {
3345    // c:3277
3346    if s.is_empty() {
3347        return (0, None);
3348    }
3349    if s[0] as char == META && s.len() >= 2 {
3350        let unmeta = s[1] ^ 0x20;
3351        (2, Some(unmeta as char))
3352    } else {
3353        (1, Some(s[0] as char))
3354    }
3355}
3356
3357/// Port of `MB_METASTRLEN(str)` from `Src/zsh.h:3279/3360`. Counts
3358/// metafied characters in the string.
3359#[inline]
3360#[allow(non_snake_case)]
3361pub fn MB_METASTRLEN(s: &str) -> usize {
3362    // c:3279
3363    let mut n = 0;
3364    let mut i = 0;
3365    let bytes = s.as_bytes();
3366    while i < bytes.len() {
3367        if bytes[i] as char == META && i + 1 < bytes.len() {
3368            i += 2;
3369        } else {
3370            i += 1;
3371        }
3372        n += 1;
3373    }
3374    n
3375}
3376
3377/// Port of `MB_METASTRWIDTH(str)` from `Src/zsh.h:3280/3361`. Counts
3378/// display width. In non-multibyte mode this is the same as
3379/// `MB_METASTRLEN`; in multibyte mode it accounts for wide chars.
3380#[inline]
3381#[allow(non_snake_case)]
3382pub fn MB_METASTRWIDTH(s: &str) -> usize {
3383    // c:3280
3384    MB_METASTRLEN(s)
3385}
3386
3387/// Port of `MB_METASTRLEN2(str, widthp)` from `Src/zsh.h:3281/3362`.
3388/// Variant that returns either char count or width depending on
3389/// `widthp`.
3390#[inline]
3391#[allow(non_snake_case)]
3392pub fn MB_METASTRLEN2(s: &str, widthp: bool) -> usize {
3393    // c:3281
3394    if widthp {
3395        MB_METASTRWIDTH(s)
3396    } else {
3397        MB_METASTRLEN(s)
3398    }
3399}
3400
3401/// Port of `MB_CHARINIT()` from `Src/zsh.h:3286/3365`. No-op
3402/// counterpart of `MB_METACHARINIT` for unmetafied input.
3403#[inline]
3404#[allow(non_snake_case)]
3405pub fn MB_CHARINIT() {} // c:3286
3406
3407/// Port of `MB_CHARLEN(str, len)` from `Src/zsh.h:3288/3367`. Byte
3408/// length of the next char in an unmetafied byte string.
3409#[inline]
3410#[allow(non_snake_case)]
3411pub fn MB_CHARLEN(s: &[u8], len: usize) -> usize {
3412    // c:3288
3413    if len == 0 || s.is_empty() {
3414        0
3415    } else {
3416        1
3417    }
3418}
3419
3420/// Port of `MB_CHARLENCONV(str, len, cp)` from `Src/zsh.h:3287/3366`.
3421/// Byte length + converted char of the next char in an unmetafied
3422/// byte string.
3423#[inline]
3424#[allow(non_snake_case)]
3425pub fn MB_CHARLENCONV(s: &[u8], len: usize) -> (usize, Option<char>) {
3426    // c:3287
3427    if len == 0 || s.is_empty() {
3428        (0, None)
3429    } else {
3430        (1, Some(s[0] as char))
3431    }
3432}
3433
3434/// Port of `WCWIDTH(wc)` from `Src/zsh.h:3300`. Display width of a
3435/// wide character. Rust uses `unicode-width`-equivalent simple rule:
3436/// 0 for control/combining, 2 for ranges typically wide (CJK), 1
3437/// otherwise.
3438#[inline]
3439#[allow(non_snake_case)]
3440pub fn WCWIDTH(wc: char) -> i32 {
3441    // c:3300
3442    if wc as u32 == 0 {
3443        return 0;
3444    }
3445    if wc.is_control() {
3446        return 0;
3447    }
3448    let cp = wc as u32;
3449    // Rough CJK-wide ranges.
3450    if (0x1100..=0x115F).contains(&cp)
3451        || (0x2E80..=0x303E).contains(&cp)
3452        || (0x3041..=0x33FF).contains(&cp)
3453        || (0x3400..=0x4DBF).contains(&cp)
3454        || (0x4E00..=0x9FFF).contains(&cp)
3455        || (0xA000..=0xA4CF).contains(&cp)
3456        || (0xAC00..=0xD7A3).contains(&cp)
3457        || (0xF900..=0xFAFF).contains(&cp)
3458        || (0xFE30..=0xFE4F).contains(&cp)
3459        || (0xFF00..=0xFF60).contains(&cp)
3460        || (0xFFE0..=0xFFE6).contains(&cp)
3461        || (0x20000..=0x2FFFD).contains(&cp)
3462        || (0x30000..=0x3FFFD).contains(&cp)
3463    {
3464        2
3465    } else {
3466        1
3467    }
3468}
3469
3470/// Port of `WCWIDTH_WINT(wc)` from `Src/zsh.h:3311/3369`. Always
3471/// 1 in non-multibyte mode; uses WCWIDTH in multibyte mode.
3472#[inline]
3473#[allow(non_snake_case)]
3474pub fn WCWIDTH_WINT(wc: char) -> i32 {
3475    // c:3311
3476    WCWIDTH(wc)
3477}
3478
3479/// Port of `IS_COMBINING(wc)` from `Src/zsh.h:3343`. True iff `wc`
3480/// is a non-zero combining character (zero display width).
3481#[inline]
3482#[allow(non_snake_case)]
3483pub fn IS_COMBINING(wc: char) -> bool {
3484    // c:3343
3485    wc as u32 != 0 && WCWIDTH(wc) == 0
3486}
3487
3488/// Port of `IS_BASECHAR(wc)` from `Src/zsh.h:3352`. True iff `wc`
3489/// is a graphic character with non-zero width (suitable as base for
3490/// a combining character).
3491#[inline]
3492#[allow(non_snake_case)]
3493pub fn IS_BASECHAR(wc: char) -> bool {
3494    // c:3352
3495    !wc.is_whitespace() && !wc.is_control() && WCWIDTH(wc) > 0
3496}
3497
3498/// Port of `ZWC(c)` from `Src/zsh.h:3328/3372`. C casts a char
3499/// literal to `wchar_t` via the `L` prefix (`L'a'`). Rust's `char`
3500/// is already 32-bit Unicode; the cast is a no-op.
3501#[inline]
3502#[allow(non_snake_case)]
3503pub const fn ZWC(c: char) -> char {
3504    c
3505} // c:3328
3506
3507// =============================================================================
3508// 46. Options accessor compat (already-allowed alias for OPT_*).
3509// =============================================================================
3510
3511/// Port of `OPT_ARG(ops, c)` from `Src/zsh.h:1412` —
3512/// `((ops)->args[((ops)->ind[c] >> 2) - 1])`. Returns the argument
3513/// associated with option `c`. Caller must have already checked
3514/// `OPT_HASARG(ops,c)`; out-of-range indices yield `None` (C would
3515/// dereference past `args[]`, which is undefined — Rust port stays
3516/// safe).
3517#[inline]
3518#[allow(non_snake_case)]
3519pub fn OPT_ARG<'a>(ops: &'a options, c: u8) -> Option<&'a str> {
3520    let idx = (ops.ind[c as usize] >> 2) as usize;
3521    if idx == 0 {
3522        return None;
3523    }
3524    ops.args.get(idx - 1).map(|s| s.as_str())
3525}
3526
3527/// Port of `OPT_ARG_SAFE(ops, c)` from `Src/zsh.h:1414` —
3528/// `(OPT_HASARG(ops,c) ? OPT_ARG(ops,c) : NULL)`.
3529#[inline]
3530#[allow(non_snake_case)]
3531pub fn OPT_ARG_SAFE<'a>(ops: &'a options, c: u8) -> Option<&'a str> {
3532    if OPT_HASARG(ops, c) {
3533        OPT_ARG(ops, c)
3534    } else {
3535        None
3536    }
3537}
3538
3539#[cfg(test)]
3540mod tests {
3541    use super::*;
3542
3543    #[test]
3544    fn zlong_zulong_sizes() {
3545        assert_eq!(std::mem::size_of::<zlong>(), 8);
3546        assert_eq!(std::mem::size_of::<zulong>(), 8);
3547    }
3548
3549    #[test]
3550    fn meta_byte_value() {
3551        assert_eq!(META as u32, 0x83);
3552    }
3553
3554    #[test]
3555    fn parser_tokens_correct() {
3556        assert_eq!(Pound as u32, 0x84);
3557        assert_eq!(Bang as u32, 0x9c);
3558        assert_eq!(Snull as u32, 0x9d);
3559        assert_eq!(Dnull as u32, 0x9e);
3560        assert_eq!(Bnull as u32, 0x9f);
3561        assert_eq!(Bnullkeep as u32, 0xa0);
3562        assert_eq!(Nularg as u32, 0xa1);
3563        assert_eq!(Marker as u32, 0xa2);
3564    }
3565
3566    #[test]
3567    fn pm_type_isolates_type_bits() {
3568        assert_eq!(PM_TYPE(PM_INTEGER | PM_EXPORTED), PM_INTEGER);
3569        assert_eq!(PM_TYPE(PM_ARRAY | PM_READONLY), PM_ARRAY);
3570    }
3571
3572    #[test]
3573    fn opt_isset_basic() {
3574        let mut ops = options {
3575            ind: [0u8; MAX_OPS],
3576            args: Vec::new(),
3577            argscount: 0,
3578            argsalloc: 0,
3579        };
3580        ops.ind[b'l' as usize] = 1; // OPT_MINUS bit
3581        assert!(OPT_ISSET(&ops, b'l'));
3582        assert!(OPT_MINUS(&ops, b'l'));
3583        assert!(!OPT_PLUS(&ops, b'l'));
3584        assert!(!OPT_ISSET(&ops, b'r'));
3585    }
3586
3587    #[test]
3588    fn binf_constants_correct() {
3589        assert_eq!(BINF_PREFIX, 1 << 5);
3590        assert_eq!(BINF_ASSIGN, 1 << 19);
3591    }
3592
3593    #[test]
3594    fn cond_constants_correct() {
3595        assert_eq!(COND_NOT, 0);
3596        assert_eq!(COND_MODI, 19);
3597    }
3598
3599    #[test]
3600    fn fdt_constants_correct() {
3601        assert_eq!(FDT_UNUSED, 0);
3602        assert_eq!(FDT_PROC_SUBST, 7);
3603        assert_eq!(FDT_TYPE_MASK, 15);
3604    }
3605
3606    #[test]
3607    fn redir_iswrite_classification() {
3608        assert!(IS_WRITE_FILE(REDIR_WRITE));
3609        assert!(IS_WRITE_FILE(REDIR_READWRITE));
3610        assert!(!IS_WRITE_FILE(REDIR_READ));
3611        assert!(IS_ERROR_REDIR(REDIR_ERRWRITE));
3612        assert!(IS_ERROR_REDIR(REDIR_ERRAPPNOW));
3613        assert!(!IS_ERROR_REDIR(REDIR_WRITE));
3614    }
3615
3616    #[test]
3617    fn wc_macros_round_trip() {
3618        let w = wc_bld(WC_LIST, 42);
3619        assert_eq!(wc_code(w), WC_LIST);
3620        assert_eq!(wc_data(w), 42);
3621    }
3622
3623    #[test]
3624    fn mb_metastrlen_counts_meta_pairs() {
3625        assert_eq!(MB_METASTRLEN("abc"), 3);
3626        // META is char 0x83, but in UTF-8 it encodes as 2 bytes
3627        // (0xC2 0x83). The byte-level metafied counter walks the
3628        // raw bytes; "abc" has 3 bytes → 3. Just test ASCII here.
3629        assert_eq!(MB_METASTRLEN("hello"), 5);
3630        assert_eq!(MB_METASTRLEN(""), 0);
3631    }
3632
3633    #[test]
3634    fn mb_charlen_basic() {
3635        assert_eq!(MB_CHARLEN(b"abc", 3), 1);
3636        assert_eq!(MB_CHARLEN(b"", 0), 0);
3637    }
3638
3639    #[test]
3640    fn wcwidth_basic() {
3641        assert_eq!(WCWIDTH('a'), 1);
3642        assert_eq!(WCWIDTH('\u{0007}'), 0); // BEL is control
3643        assert_eq!(WCWIDTH('\u{4E2D}'), 2); // CJK
3644    }
3645
3646    #[test]
3647    fn is_combining_zero_width() {
3648        assert!(!IS_COMBINING('a')); // width 1
3649        assert!(!IS_COMBINING('\u{0000}')); // null returns false per c:3343
3650                                            // Note: the WCWIDTH heuristic in this port doesn't recognise
3651                                            // combining marks — that needs a unicode-width table. Test
3652                                            // the contract (width-0 non-zero char) rather than the
3653                                            // specific Unicode codepoint behaviour.
3654                                            // BEL (control) returns 0 from WCWIDTH and is non-zero, so
3655                                            // IS_COMBINING returns true.
3656        assert!(IS_COMBINING('\u{0007}'));
3657    }
3658
3659    #[test]
3660    fn pat_flags_correct() {
3661        assert_eq!(PAT_FILE, 0x0001);
3662        assert_eq!(PAT_LCMATCHUC, 0x1000);
3663    }
3664
3665    #[test]
3666    fn sub_flags_correct() {
3667        assert_eq!(SUB_END, 0x0001);
3668        assert_eq!(SUB_EGLOB, 0x4000);
3669    }
3670
3671    #[test]
3672    fn pp_constants_ordered() {
3673        assert_eq!(PP_FIRST, PP_ALPHA);
3674        assert!(PP_LAST >= PP_ALPHA);
3675        assert!(PP_RANGE > PP_LAST);
3676    }
3677
3678    #[test]
3679    fn typeset_optstr_constants() {
3680        assert_eq!(TYPESET_OPTSTR, "aiEFALRZlurtxUhHT");
3681        assert_eq!(TYPESET_OPTNUM, "LRZiEF");
3682    }
3683
3684    #[test]
3685    fn job_stat_flags_distinct() {
3686        let all = STAT_CHANGED
3687            | STAT_STOPPED
3688            | STAT_TIMED
3689            | STAT_DONE
3690            | STAT_LOCKED
3691            | STAT_NOPRINT
3692            | STAT_INUSE
3693            | STAT_SUPERJOB
3694            | STAT_SUBJOB
3695            | STAT_WASSUPER
3696            | STAT_CURSH
3697            | STAT_NOSTTY
3698            | STAT_ATTACH
3699            | STAT_SUBLEADER
3700            | STAT_BUILTIN
3701            | STAT_SUBJOB_ORPHANED
3702            | STAT_DISOWN;
3703        assert_eq!(all.count_ones(), 17);
3704    }
3705
3706    #[test]
3707    fn opt_size_at_186() {
3708        assert_eq!(OPT_SIZE, 186);
3709    }
3710
3711    #[test]
3712    fn cs_count_is_32() {
3713        assert_eq!(CS_COUNT, 32);
3714    }
3715
3716    #[test]
3717    fn zwc_passes_through() {
3718        assert_eq!(ZWC('a'), 'a');
3719    }
3720}
3721
3722// Suppress dead-code warnings for the AtomicI32 we don't use yet.
3723#[allow(dead_code)]
3724const _MARKER_KEEP: AtomicI32 = AtomicI32::new(0);