Expand description
pattern_data_escape submodule (Rust-only; see the module docs).
Rust-only utility (NOT a port — lives outside src/ported/ by design).
The DATA half of docs/BUGS.md #1090: how a backslash that is a
CHARACTER OF A VALUE has to be spelled before it reaches
ported::pattern::patcompile.
C never needs this transform. Its pattern compiler consumes the
LEXER’s encoding, where a source-level quote already arrived as
Bnull/Bnullkeep + payload (c:Src/zsh.h:195-200), so a RAW
backslash in patcompile’s input can only be data. A substituted
value acquires its pattern meaning in zshtokenize
(c:Src/glob.c:3585-3653), reached from strcatsub’s
if (glbsub) shtokenize(dest) (c:Src/subst.c:822/830) for
${~spec} / GLOB_SUBST, and that function rewrites a backslash
into a quote marker ONLY when the next character reaches its
ztokens scan:
c:Src/glob.c:3597-3605 case Bnull: case Bnullkeep: case '\\':
if (bslash) { s[-1] = … Bnullkeep/Bnull; break; }
bslash = 1; continue;
c:Src/glob.c:3640-3648 for (t = ztokens; *t; t++)
if (*t == *s) {
if (bslash) s[-1] = … Bnullkeep/Bnull;
else *s = (t - ztokens) + Pound;
break;
}
c:Src/glob.c:3651 bslash = 0;Before anything else — a space, a $, a { — no switch arm fires,
c:3651 just clears bslash, and BOTH bytes survive in the string as
ordinary literal data. That is why real zsh answers
p='a\ b'; [[ 'a b' == ${~p} ]] # no match — the pattern holds a backslash
p='a\ b'; [[ 'a\ b' == ${~p} ]] # matchported::pattern’s input normalizer (src/ported/pattern.rs, the \\
arm) reads a lone raw \X as a QUOTE of X — the spelling every
SOURCE-level pattern path in zshrs hands it (the cond/case pattern
builder in extensions::compile_zsh, ${v//\%/%%}’s builder in
ported::subst) — and spells a literal backslash as the pair \\.
So doubling exactly the backslashes zshtokenize declines to consume
is what carries C’s Bnull-vs-raw split into the Rust encoding.
Backslashes the tokenizer WOULD consume are left in place so the
downstream tokenizer/normalizer still folds them into a quote at
their original position.
Callers are the “this pattern text came out of a VALUE” sites:
ported::subst::paramsubst— the search-subscript patterns (${a[(I)…]}/(i)/(r)/(R)/(K)), which reachpatcompilethroughtokenizealone (c:Src/params.c:1727).fusevm_bridge’sBUILTIN_GLOB_SUBST_GUARD/BUILTIN_PAT_DATA_BACKSLASH— the${~spec}andsetopt globsubstlegs of a[[ … == pat ]]RHS and acasearm, thestrcatsubshtokenizeC runs at c:Src/subst.c:822/830.
Functions§
- dropin_
keeps_ ksh_ groups - Whether a bash DROP-IN is running with extended patterns on.
- dropin_
source_ pattern_ parens_ literal - Whether a SOURCE-level
[[ … ]]/casepattern must follow the EMULATED shell’s paren rules instead of zsh’s. - escape_
data_ backslashes - Rewrite a pattern string that came out of a VALUE so
ported::pattern’s normalizer reads its backslashes the wayzshtokenizedoes — see the module docs. - escape_
shglob_ parens - The SH_GLOB half of the same
strcatsubstep:shtokenizebuilds its flags from the option (c:Src/glob.c:3575-3580) - escape_
tokenized_ data_ backslashes - The same DATA-vs-QUOTE split as
escape_data_backslashes, applied one step LATER: to a string that has ALREADY been throughtokenize()+remnulargs()and is about to be handed straight topatcompile. - ksh93_
flagged_ match - ksh93 pattern options:
[[ ab == ~(i)AB ]],~(i:AB)c,a~(i)B*,~(E)b+,~(F)a.c,~(G)a\(b\)c.Some(verdict)whenpatcarries a~(options)prefix this port models,Noneto let the ordinary matcher run. Verified against ksh93u+m 1.0.10: - note_
default_ word_ bars - !!! WARNING: RUST-ONLY HELPER — NO C COUNTERPART !!!
- shglob_
hides_ parens - Whether SH_GLOB’s
(/|/)suppression applies at all right now.