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