nx_tools_argv_unicode_gate.nx source
↩ module page · 224 lines · 12718 B
1import "nx_tools_api.nx"
2import "nx_gate_verdict.nx"
3
4func tug_case(label: *u8, body: *u8, want: i64, expected: *u8, cap: i64, maxn: i64, ctr: *i64) -> i64 {
5 let scratch: *u8 = sys_mmap(cap + 8)
6 let av: *i64 = sys_mmap((maxn + 3)*8) as *i64
7 var j: i64 = 0
8 while j < maxn + 3 { av[j] = 85; j = j + 1 }
9 j = cap
10 while j < cap + 8 { scratch[j] = 85 as u8; j = j + 1 }
11 let result: i64 = ta_json_str_array(body, jx_len(body), "\"argv\"" as *u8, av, 1, maxn, scratch, cap)
12 var ok: i64 = result == want
13 var ep: *u8 = expected
14 var k: i64 = 0
15 if ok == 1 { if result >= 0 { if result <= maxn {
16 while k < result {
17 let addr: i64 = av[k + 1]
18 var valid_ptr: i64 = 1
19 if addr < (scratch as i64) { valid_ptr = 0 }
20 if addr >= (scratch as i64) + cap { valid_ptr = 0 }
21 if valid_ptr == 0 { ok = 0 }
22 else {
23 let got: *u8 = addr as *u8
24 let remain: i64 = cap - (addr - (scratch as i64))
25 var gn: i64 = 0
26 var scan: i64 = 1
27 while scan == 1 {
28 if gn >= remain { scan = 0 }
29 else { if got[gn] == (0 as u8) { scan = 0 } else { gn = gn + 1 } }
30 }
31 let en: i64 = jx_len(ep)
32 if gn != en { ok = 0 }
33 if gn >= remain { ok = 0 }
34 j = 0
35 while j < gn { if j < en { if got[j] != ep[j] { ok = 0 } } j = j + 1 }
36 ep = ((ep as i64) + en + 1) as *u8
37 }
38 k = k + 1
39 }
40 j = result + 1
41 while j <= maxn { if av[j] != 85 { ok = 0 } j = j + 1 }
42 } else { ok = 0 } } }
43 if av[0] != 85 { ok = 0 }
44 if av[maxn + 1] != 85 { ok = 0 }
45 if av[maxn + 2] != 85 { ok = 0 }
46 j = cap
47 while j < cap + 8 { if scratch[j] != (85 as u8) { ok = 0 }; j = j + 1 }
48 gv_check(label, ok, ctr)
49 return ok
50}
51
52// Frozen current-original loop is a test oracle for unchanged legacy modes, not a runtime decoder.
53func tug_original_ctl(buf: *u8, n: i64, vstart: i64, out: *u8, outcap: i64, keep_ctl: i64) -> i64 {
54 var i: i64 = vstart
55 var o: i64 = 0
56 while i < n {
57 let c: i64 = buf[i] as i64
58 if c == 92 { // backslash escape
59 if i + 1 < n {
60 var e: i64 = buf[i + 1] as i64
61 if e == 117 { // \uXXXX
62 var cp: i64 = jx_hex4(buf, n, i + 2)
63 if cp < 0 {
64 // malformed escape: emit nothing for it and step past the 'u' rather than
65 // re-emitting the digits as text (that is exactly the old corruption).
66 i = i + 2
67 } else {
68 var adv: i64 = 6
69 if cp >= 55296 { if cp < 56320 { // high surrogate D800-DBFF
70 if i + 7 < n { if (buf[i + 6] as i64) == 92 { if (buf[i + 7] as i64) == 117 {
71 let lo: i64 = jx_hex4(buf, n, i + 8)
72 if lo >= 56320 { if lo < 57344 {
73 cp = 65536 + ((cp - 55296) * 1024) + (lo - 56320)
74 adv = 12
75 } }
76 } } }
77 } }
78 if keep_ctl == 0 {
79 if cp == 9 { cp = 32 }
80 if cp == 10 { cp = 32 }
81 if cp == 13 { cp = 32 }
82 }
83 o = jx_emit_utf8(out, o, outcap, cp)
84 i = i + adv
85 }
86 } else {
87 if keep_ctl == 1 {
88 if e == 116 { e = 9 } // t -> TAB
89 if e == 110 { e = 10 } // n -> LF
90 if e == 114 { e = 13 } // r -> CR
91 if e == 98 { e = 8 } // b -> BS
92 if e == 102 { e = 12 } // f -> FF
93 } else {
94 if e == 116 { e = 32 } // t -> space
95 if e == 110 { e = 32 } // n -> space
96 if e == 114 { e = 32 } // r -> space
97 }
98 if o < outcap - 1 { out[o] = e as u8; o = o + 1 }
99 i = i + 2
100 }
101 } else { i = i + 1 }
102 } else {
103 if c == 34 { out[o] = 0 as u8; return i + 1 } // closing quote
104 var cc: i64 = c
105 if keep_ctl == 0 {
106 if cc == 9 { cc = 32 }
107 if cc == 10 { cc = 32 }
108 if cc == 13 { cc = 32 }
109 }
110 if o < outcap - 1 { out[o] = cc as u8; o = o + 1 }
111 i = i + 1
112 }
113 }
114 out[o] = 0 as u8
115 return 0 - 1
116}
117
118func tug_legacy(label: *u8, src: *u8, cap: i64, ctr: *i64) -> i64 {
119 let a: *u8 = sys_mmap(cap + 8); let b: *u8 = sys_mmap(cap + 8)
120 var mode: i64 = 0; var ok: i64 = 1
121 while mode <= 1 {
122 var j: i64 = 0
123 while j < cap + 8 { a[j] = 85 as u8; b[j] = 85 as u8; j = j + 1 }
124 let before: i64 = tug_original_ctl(src, jx_len(src), 0, a, cap, mode)
125 let after: i64 = jx_copy_str_ctl(src, jx_len(src), 0, b, cap, mode)
126 if before != after { ok = 0 }
127 j = 0
128 while j < cap + 8 { if a[j] != b[j] { ok = 0 } j = j + 1 }
129 mode = mode + 1
130 }
131 gv_check(label, ok, ctr); return ok
132}
133
134func main() -> i64 {
135 let ctr: *i64 = gv_ctr()
136 gv_head("TOOLS-ARGV-UNICODE" as *u8)
137 let eacute: *u8 = sys_mmap(3)
138 eacute[0] = 195 as u8; eacute[1] = 169 as u8; eacute[2] = 0 as u8
139 let euro: *u8 = sys_mmap(4)
140 euro[0] = 226 as u8; euro[1] = 130 as u8; euro[2] = 172 as u8; euro[3] = 0 as u8
141 let astral: *u8 = sys_mmap(5)
142 astral[0] = 240 as u8; astral[1] = 159 as u8; astral[2] = 152 as u8; astral[3] = 128 as u8; astral[4] = 0 as u8
143 let unicode: *u8 = sys_mmap(10)
144 var u: i64 = 0
145 while u < 2 { unicode[u] = eacute[u]; u = u + 1 }
146 u = 0
147 while u < 3 { unicode[2 + u] = euro[u]; u = u + 1 }
148 u = 0
149 while u < 4 { unicode[5 + u] = astral[u]; u = u + 1 }
150 unicode[9] = 0 as u8
151 let rawbody: *u8 = sys_mmap(32)
152 var rawlen: i64 = ta_cat(rawbody, 0, "{\"argv\":[\"" as *u8)
153 u = 0
154 while u < 9 { rawbody[rawlen + u] = unicode[u]; u = u + 1 }
155 rawlen = ta_cat(rawbody, rawlen + 9, "\"]}" as *u8)
156 rawbody[rawlen] = 0 as u8
157 let pair: *u8 = sys_mmap(4)
158 pair[0] = 97 as u8; pair[1] = 0 as u8; pair[2] = 98 as u8; pair[3] = 0 as u8
159 let controls: *u8 = sys_mmap(6)
160 controls[0] = 8 as u8; controls[1] = 12 as u8; controls[2] = 10 as u8
161 controls[3] = 13 as u8; controls[4] = 9 as u8; controls[5] = 0 as u8
162 tug_case("literal greater-than" as *u8, "{\"argv\":[\">\"]}" as *u8, 1, ">" as *u8, 64, 4, ctr)
163 tug_case("PowerShell Unicode escape exact" as *u8, "{\"argv\":[\"\\u003e\"]}" as *u8, 1, ">" as *u8, 64, 4, ctr)
164 tug_case("uppercase hex escape" as *u8, "{\"argv\":[\"\\u003E\"]}" as *u8, 1, ">" as *u8, 64, 4, ctr)
165 tug_case("BMP two-byte" as *u8, "{\"argv\":[\"\\u00e9\"]}" as *u8, 1, eacute, 64, 4, ctr)
166 tug_case("BMP three-byte" as *u8, "{\"argv\":[\"\\u20ac\"]}" as *u8, 1, euro, 64, 4, ctr)
167 tug_case("surrogate pair" as *u8, "{\"argv\":[\"\\uD83D\\uDE00\"]}" as *u8, 1, astral, 64, 4, ctr)
168 tug_case("literal UTF-8 unchanged" as *u8, rawbody, 1, unicode, 64, 4, ctr)
169 tug_case("isolated high surrogate refused" as *u8, "{\"argv\":[\"\\ud800\"]}" as *u8, -1, "" as *u8, 64, 4, ctr)
170 tug_case("isolated low surrogate refused" as *u8, "{\"argv\":[\"\\udc00\"]}" as *u8, -1, "" as *u8, 64, 4, ctr)
171 tug_case("wrong surrogate pair refused" as *u8, "{\"argv\":[\"\\ud800\\u0041\"]}" as *u8, -1, "" as *u8, 64, 4, ctr)
172 tug_case("invalid hex refused" as *u8, "{\"argv\":[\"\\u00xz\"]}" as *u8, -1, "" as *u8, 64, 4, ctr)
173 tug_case("short Unicode refused" as *u8, "{\"argv\":[\"\\u123\"]}" as *u8, -1, "" as *u8, 64, 4, ctr)
174 tug_case("unknown escape refused" as *u8, "{\"argv\":[\"\\q\"]}" as *u8, -1, "" as *u8, 64, 4, ctr)
175 tug_case("NUL escape refused" as *u8, "{\"argv\":[\"\\u0000\"]}" as *u8, -1, "" as *u8, 64, 4, ctr)
176 tug_case("escaped controls retained" as *u8, "{\"argv\":[\"\\b\\f\\n\\r\\t\"]}" as *u8, 1, controls, 64, 4, ctr)
177 tug_case("quote backslash slash retained" as *u8, "{\"argv\":[\"\\\"\\\\\\/\"]}" as *u8, 1, "\"\\/" as *u8, 64, 4, ctr)
178 tug_case("raw newline refused" as *u8, "{\"argv\":[\"a\nb\"]}" as *u8, -1, "" as *u8, 64, 4, ctr)
179 tug_case("scratch exact ASCII fit" as *u8, "{\"argv\":[\"abc\"]}" as *u8, 1, "abc" as *u8, 4, 4, ctr)
180 tug_case("scratch shortage refused" as *u8, "{\"argv\":[\"abc\"]}" as *u8, -1, "" as *u8, 3, 4, ctr)
181 tug_case("scratch exact astral fit" as *u8, "{\"argv\":[\"\\ud83d\\ude00\"]}" as *u8, 1, astral, 5, 4, ctr)
182 tug_case("scratch astral shortage refused" as *u8, "{\"argv\":[\"\\ud83d\\ude00\"]}" as *u8, -1, "" as *u8, 4, 4, ctr)
183 tug_case("empty argument exact fit" as *u8, "{\"argv\":[\"\"]}" as *u8, 1, "" as *u8, 1, 4, ctr)
184 tug_case("argument maximum exact" as *u8, "{\"argv\":[\"a\",\"b\"]}" as *u8, 2, pair, 64, 2, ctr)
185 tug_case("argument maximum exceeded" as *u8, "{\"argv\":[\"a\",\"b\",\"c\"]}" as *u8, -1, "" as *u8, 64, 2, ctr)
186 tug_case("empty array" as *u8, "{\"argv\":[]}" as *u8, 0, "" as *u8, 64, 4, ctr)
187 tug_case("absent array" as *u8, "{}" as *u8, 0, "" as *u8, 64, 4, ctr)
188 tug_case("nonarray refused" as *u8, "{\"argv\":\"a\"}" as *u8, -1, "" as *u8, 64, 4, ctr)
189 tug_case("nonstring member refused" as *u8, "{\"argv\":[1]}" as *u8, -1, "" as *u8, 64, 4, ctr)
190 tug_case("missing comma refused" as *u8, "{\"argv\":[\"a\" \"b\"]}" as *u8, -1, "" as *u8, 64, 4, ctr)
191 tug_case("trailing comma refused" as *u8, "{\"argv\":[\"a\",]}" as *u8, -1, "" as *u8, 64, 4, ctr)
192 tug_case("unterminated string refused" as *u8, "{\"argv\":[\"abc" as *u8, -1, "" as *u8, 64, 4, ctr)
193 tug_case("unterminated array refused" as *u8, "{\"argv\":[\"abc\"" as *u8, -1, "" as *u8, 64, 4, ctr)
194 tug_case("missing colon refused" as *u8, "{\"argv\"[\"abc\"]}" as *u8, -1, "" as *u8, 64, 4, ctr)
195 tug_case("whitespace syntax accepted" as *u8, "{\"argv\" \r\n : \t [ \"abc\" ]}" as *u8, 1, "abc" as *u8, 64, 4, ctr)
196 let src: *u8 = "a\\nb\"" as *u8
197 let out: *u8 = sys_mmap(32)
198 let tsv_end: i64 = jx_copy_str(src, jx_len(src), 0, out, 32)
199 gv_check("legacy TSV controls stay spaces" as *u8, out[1] == (32 as u8), ctr)
200 let raw_end: i64 = jx_copy_str_raw(src, jx_len(src), 0, out, 32)
201 gv_check("legacy raw controls stay newline" as *u8, out[1] == (10 as u8), ctr)
202 gv_check("legacy wrappers retain ending cursor" as *u8, tsv_end == raw_end, ctr)
203 // Invalid raw UTF-8 cannot be represented as a source string literal: inject exact bytes.
204 let bad: *u8 = sys_mmap(3)
205 bad[0] = 192 as u8; bad[1] = 128 as u8; bad[2] = 34 as u8
206 gv_check("overlong UTF-8 refused" as *u8, jx_copy_str_argv(bad, 3, 0, out, 32) < 0, ctr)
207 bad[0] = 237 as u8; bad[1] = 160 as u8; bad[2] = 128 as u8
208 gv_check("raw UTF-8 surrogate refused" as *u8, jx_copy_str_argv(bad, 3, 0, out, 32) < 0, ctr)
209 tug_legacy("legacy ASCII output/cursor" as *u8, "abc\"" as *u8, 16, ctr)
210 tug_legacy("legacy unknown escape behavior" as *u8, "\\q\"" as *u8, 16, ctr)
211 tug_legacy("legacy escaped controls behavior" as *u8, "\\b\\f\\n\\r\\t\"" as *u8, 16, ctr)
212 tug_legacy("legacy raw controls behavior" as *u8, "a\nb\rc\td\"" as *u8, 16, ctr)
213 tug_legacy("legacy Unicode BMP behavior" as *u8, "\\u00e9\\u20ac\"" as *u8, 16, ctr)
214 tug_legacy("legacy surrogate pair behavior" as *u8, "\\ud83d\\ude00\"" as *u8, 16, ctr)
215 tug_legacy("legacy lone surrogate behavior" as *u8, "\\ud800\"" as *u8, 16, ctr)
216 tug_legacy("legacy malformed Unicode behavior" as *u8, "\\u00xz\"" as *u8, 16, ctr)
217 tug_legacy("legacy output truncation behavior" as *u8, "abcdef\"" as *u8, 3, ctr)
218 tug_legacy("legacy Unicode truncation behavior" as *u8, "\\ud83d\\ude00x\"" as *u8, 3, ctr)
219 tug_legacy("legacy trailing backslash behavior" as *u8, "abc\\" as *u8, 16, ctr)
220 tug_legacy("legacy unterminated behavior" as *u8, "abc" as *u8, 16, ctr)
221 tug_legacy("legacy minimum capacity behavior" as *u8, "abc\"" as *u8, 1, ctr)
222 let rc: i64 = gv_verdict("TOOLS-ARGV-UNICODE" as *u8, ctr, "native argv preserves exact Unicode and escaped controls; malformed or truncated arguments are refused" as *u8)
223 sys_exit(rc); return rc
224}