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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// Copyright (c) 2025-2026 the libmagic-rs contributors
// SPDX-License-Identifier: Apache-2.0
//! String-family bareword value parsing for magic file rules.
//!
//! Covers the value-dispatch fallback used by `string`/`pstring`/`string16`/
//! `search` rules (see GOTCHAS S3.6, S3.12, S6.7): a leading quoted-string
//! or hex/escape-byte attempt, followed by a bareword fallback that never
//! interprets the token as a number. Extracted from `grammar/mod.rs` as a
//! pure code-motion split (issue #391 Unit U4) -- no behavior changes; the
//! getstr/regex special-casing and drop-backslash escape logic are moved
//! intact.
use ;
use crateValue;
use value;
/// Parse the comparison value for a string-family type.
///
/// libmagic never interprets a `string`/`pstring`/`string16`/`search`
/// comparison value as a number: `0 string >0` compares against the
/// literal ASCII byte `'0'` (0x30), and `>0.6.1` against the literal
/// characters `0.6.1` -- not the integer 0 or the float 0.6. The generic
/// [`super::value::parse_value`] tries its float and integer branches (see
/// `value::parse_value`) before falling through, so a bareword like `0` or
/// `0.6.1` was captured as `Value::Uint`/`Value::Float`. A subsequent
/// comparison against the string field read from the file then yields no
/// ordering (`String` vs `Uint`/`Float` is incomparable), so the rule
/// silently never matched -- breaking real `>0` idioms such as
/// `\b, name %s` / `face %s` / `palette %s` and version compares like
/// `>0.6.1 ... version %s`.
///
/// This parser mirrors [`super::value::parse_value`]'s ordering for the two branches
/// that are correct for string-family values -- a leading whitespace trim,
/// then a quoted string (-> `Value::String`), then a hex/escape byte
/// sequence (-> `Value::Bytes`, e.g. gzip's `\037\213` or `\177ELF`) -- but
/// replaces the numeric (float/integer) branches with
/// [`parse_bare_string_value`], so every remaining bareword resolves to a
/// `Value::String`. The leading `multispace0` is load-bearing: it ensures
/// the hex branch sees byte-identical input to what `parse_value` fed it,
/// so an escape-heavy value cannot fall through to the lossy-UTF-8
/// `parse_bare_string_value` path and corrupt a high byte (see the
/// `high-byte-utf8-corruption-class` note).
///
/// Hex-*letter* barewords (`>AB`, `cafebabe`) still resolve to
/// `Value::Bytes` via the unchanged hex branch, matching GOTCHAS S3.12 --
/// only the numeric subset changes here.
///
/// # Errors
/// Returns a nom parsing error only when the value is empty/whitespace-only
/// (via [`parse_bare_string_value`]); quoted and hex forms are attempted
/// first and never error out of this function on a non-empty token.
pub
/// Parse a bare (unquoted) single-token string literal as a `Value::String`.
///
/// Used only as a fallback for string-family types (`string`, `pstring`,
/// `search`) when the strict [`super::value::parse_value`] alternatives all
/// fail. (`regex` never reaches here -- it is intercepted earlier by the
/// dedicated getstr branch, see GOTCHAS S2.12.) Consumes leading whitespace, then reads a run of non-whitespace
/// characters as the literal value, **interpreting magic(5) escape
/// sequences** along the way: `\0`, `\n`, `\r`, `\t`, `\\`, `\"`, `\'`,
/// `\NNN` (3-digit octal), and `\xNN` (hex). This supports magic-file
/// rules like `0 string PNCIHISK\0 ...` where the trailing `\0` denotes
/// a literal NUL byte that must be present in the file.
///
/// Without escape interpretation, the comparison value stored in the
/// AST is the literal six-byte string `\` + `0` instead of `\x00`, and
/// the rule never matches against a real on-disk byte sequence ending
/// in NUL. This was a regression that prevented even simple top-level
/// rules from matching when loaded from a magic file.
///
/// # Errors
/// Returns a nom parsing error if the input contains no non-whitespace
/// token (e.g. it is empty or consists entirely of whitespace).
pub