code wiki / _hdl_build / nx_cms_multipart.nx
nx_cms_multipart.nx source
↩ module page · 98 lines · 4017 B
1// nx_cms_multipart.nx -- CORRECT multipart/form-data part finder for the CMS uploads rung (C11).
2// The shipped runtime/nx_multipart.nx has a closing-delimiter/CRLF-alignment bug (its main() is a
3// compile-only smoke, never gated -- multipart_parse returns -1 on a well-formed 2-part body, proven
4// 2026-06-10 by _cms_mp_probe). Per rule 3 (no patch cascades) this is a clean rewrite with a real
5// KAT, not a patch. Flat i64 output (4 cells/part: headers_off, headers_len, body_off, body_len) so
6// there is zero struct-pointer-arithmetic ambiguity. Zero-copy: cells are offsets into the caller's
7// buffer. license_tier: ORIGINAL
8import "nx_syscalls.nx"
9
10// first index of needle[0..nl) in buf[start..n), or -1
11func cmp_find(buf: *u8, start: i64, n: i64, needle: *u8, nl: i64) -> i64 {
12 if nl == 0 { return start }
13 var i: i64 = start
14 while i + nl <= n {
15 var j: i64 = 0
16 var ok: i64 = 1
17 while j < nl { if buf[i + j] != needle[j] { ok = 0; j = nl } else { j = j + 1 } }
18 if ok == 1 { return i }
19 i = i + 1
20 }
21 return 0 - 1
22}
23
24// parse multipart body. boundary = raw boundary WITHOUT leading -- (the Content-Type value).
25// out holds 4 i64 per part. Returns part count, or -1 on malformed. cap = max parts.
26func cms_mp_parse(buf: *u8, n: i64, boundary: *u8, blen: i64, out: *i64, cap: i64) -> i64 {
27 // delim = "--" + boundary ; crlf_delim = "\r\n" + delim
28 let delim: *u8 = sys_mmap(256)
29 delim[0] = 45 as u8; delim[1] = 45 as u8
30 var i: i64 = 0
31 while i < blen { delim[2 + i] = boundary[i]; i = i + 1 }
32 let dl: i64 = 2 + blen
33 let crlfd: *u8 = sys_mmap(256)
34 crlfd[0] = 13 as u8; crlfd[1] = 10 as u8
35 var k: i64 = 0
36 while k < dl { crlfd[2 + k] = delim[k]; k = k + 1 }
37 let cdl: i64 = 2 + dl
38
39 let first: i64 = cmp_find(buf, 0, n, delim, dl)
40 if first < 0 { return 0 - 1 }
41 var cur: i64 = first
42 var count: i64 = 0
43 var going: i64 = 1
44 while going == 1 {
45 var p: i64 = cur + dl
46 // closing delimiter "--BOUNDARY--" ?
47 var closed: i64 = 0
48 if p + 2 <= n { if (buf[p] as i64) == 45 { if (buf[p+1] as i64) == 45 { closed = 1 } } }
49 if closed == 1 { going = 0 }
50 if closed == 0 {
51 // expect CRLF after the boundary line
52 if p + 2 > n { return 0 - 1 }
53 if (buf[p] as i64) != 13 { return 0 - 1 }
54 if (buf[p+1] as i64) != 10 { return 0 - 1 }
55 p = p + 2
56 let he: i64 = cmp_find(buf, p, n, "\r\n\r\n" as *u8, 4)
57 if he < 0 { return 0 - 1 }
58 let body_off: i64 = he + 4
59 let nb: i64 = cmp_find(buf, body_off, n, crlfd, cdl)
60 if nb < 0 { return 0 - 1 }
61 if count >= cap { return 0 - 1 }
62 out[count*4] = p
63 out[count*4+1] = he - p
64 out[count*4+2] = body_off
65 out[count*4+3] = nb - body_off
66 count = count + 1
67 cur = nb + 2 // skip CRLF -> cur points at the next "--BOUNDARY"
68 }
69 }
70 return count
71}
72
73// copy the value following key (e.g. " name=\"" or "filename=\"") inside buf[off..end), up to the
74// next '"', into out (NUL-terminated). Returns length, or -1 if key absent. (Content-Disposition
75// field extraction.)
76func cms_mp_field(buf: *u8, off: i64, end: i64, key: *u8, out: *u8, cap: i64) -> i64 {
77 var kl: i64 = 0
78 while key[kl] != (0 as u8) { kl = kl + 1 }
79 var i: i64 = off
80 while i + kl <= end {
81 var q: i64 = 0
82 var ok: i64 = 1
83 while q < kl { if (buf[i+q] as i64) != (key[q] as i64) { ok = 0; q = kl } else { q = q + 1 } }
84 if ok == 1 {
85 var pp: i64 = i + kl
86 var o: i64 = 0
87 while pp < end {
88 let c: i64 = buf[pp] as i64
89 if c == 34 { out[o] = 0 as u8; return o }
90 if o < cap { out[o] = c as u8; o = o + 1 }
91 pp = pp + 1
92 }
93 return 0 - 1
94 }
95 i = i + 1
96 }
97 return 0 - 1
98}