1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//! !!! WARNING: RUST-ONLY MODULE — NO C COUNTERPART !!!
//!
//! The METAFIED byte spelling of a shell string, for the `zstrcmp`
//! call sites whose C operands are metafied.
//!
//! C's `zstrcmp` (`Src/sort.c:191`) collates whatever bytes its caller
//! hands it, and the callers disagree on the form:
//!
//! | call site | operand |
//! |---------------------------------------------|-------------|
//! | `strmetasort` `Src/sort.c:299-315` | unmetafied |
//! | `gmatchcmp` GS_NAME `Src/glob.c:945` | unmetafied (`uname`, c:1963-1973) |
//! | `cd_sort` `Src/Zle/computil.c:235` | unmetafied (`sortstr`, c:301-302) |
//! | `gmatchcmp` GS_EXEC `Src/glob.c:981` | METAFIED (`getsparam("REPLY")`, c:1938-1941) |
//! | `matchcmp` `Src/Zle/compcore.c:3194` | METAFIED (`Cmatch->str` / `->disp`) |
//!
//! Metafication rewrites `{0x00} ∪ [0x83, 0xa2]` as `Meta`, `b ^ 32`
//! (`Src/utils.c:4195-4201`, `metafy` c:4856). Those bytes sit inside
//! UTF-8 continuation ranges, so for most non-ASCII text the metafied
//! operand is not valid multibyte and `strcoll` collates it byte-wise.
//! zshrs keeps strings unmetafied, so the two metafied call sites have to
//! rebuild C's bytes before comparing or a UTF-8 locale reorders them.
//!
//! The ported `utils::metafy` returns a `String` and is lossy on exactly
//! these inputs (a metafied `日` is `e6 83 b7 a5`), and `src/ported/` may
//! not gain functions, which is why this lives here.
use Cow;
/// The bytes C would hold for `s` after `metafy()` (`Src/utils.c:4856`).
///
/// `s` is first taken back to its raw bytes with `unmetafy_str`, because a
/// zshrs `String` carries a raw non-UTF-8 byte as the char pair
/// `U+0083`, `U+00(b ^ 32)`. Borrows when `s` is ASCII: metafication is
/// the identity there, and this runs inside sort comparators.