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}