code wiki / _hdl_build / nx_uiq_responsive.nx

nx_uiq_responsive.nx source

↩ module page · 466 lines · 20061 B

1// nx_uiq_responsive.nx -- THE SEVENTH nx_uiq_* RULER: the responsive-layout auditor the family lacked. 2// 3// WHY THIS EXISTS (measured 2026-08-10): the estate had SIX nx_uiq_* rulers (contrast/wire/perf/visual/ 4// atree/sentinel) and NOT ONE measured responsive layout. nx_uiq_perf's only "media" reference is 5// `unsized_media` (a CLS proxy). Consequence, measured on the live site: nx_page_verify returned 6// VERDICT=GREEN / a11y-issues=0 on nishifamily.com/games while that page's Connect Four board is a 7// FIXED 368px grid that overflows every phone -- and it had been live for ~53 days. 8// => A GREEN FROM AN INSTRUMENT THAT CANNOT SEE THE DEFECT READS AS "THE PAGE IS FINE". 9// 10// THE RULER IS NOT INVENTED (family doctrine: name a real external bar, never a taste). 11// PRIMARY : W3C WCAG 2.1 SC 1.4.10 "Reflow" (Level AA) -- content must reflow to 320 CSS px 12// WITHOUT requiring scrolling in two dimensions. => a fixed-track grid whose own 13// minimum width exceeds 320px cannot conform, whatever the rest of the page does. 14// SECONDARY: SC 2.5.8 "Target Size (Minimum)" (AA) = 24x24 CSS px floor for pointer targets. 15// SECONDARY: SC 1.4.4 "Resize Text" (AA) -- suppressing zoom (user-scalable=no / maximum-scale=1) 16// fails it outright. 17// 18// WHAT IT COMPUTES (arithmetic, never a vibe). For every `repeat(<N>,<W>px)` track list it finds the 19// ENCLOSING RULE BLOCK and sums that grid's own intrinsic minimum width: 20// min_width = N*W + (N-1)*gap + 2*padding_horizontal 21// The live Connect Four board is the worked example this organ was built against: 22// 7*46 + 6*5 + 2*8 = 322 + 30 + 16 = 368 > 320 => RED 23// `repeat(auto-fill,minmax(...))` and other non-numeric counts parse as NOT-FIXED and are skipped -- 24// that shape IS responsive, so counting it would be a false positive. 25// 26// DECLARED IMPRECISION (family law: document the imprecision or the next reader trusts it as exact): 27// * The gate compares the grid's OWN min width against 320 and deliberately does NOT add ancestor 28// container padding. Attributing padding across nesting needs a cascade model a flat scan does not 29// have, and over-counting it would manufacture false positives. So T1 is a LOWER BOUND on the width 30// actually required: it fires only when the board ALONE cannot fit. `container_pad_max` is REPORTED 31// (not gated) so a reader can see the real budget is 320 - that. 32// * Tap-target sizing is read from blocks declaring `cursor:pointer`. A target sized purely by content 33// or by a shorthand this scan does not model is reported as UNKNOWN, never as a pass. 34// * This is a STATIC auditor. It proves the source-visible causes of a reflow failure; it does not 35// render. A PASS here is NECESSARY, NOT SUFFICIENT -- same honest envelope as nx_uiq_perf. 36// 37// Composes the family lib (DRY: io + find + syscalls) rather than re-implementing them. 38// usage: nx_uiq_responsive --kat | <html-file-path> 39// exit : 0 GREEN | 1 RED | 2 cannot-read <- THE EXIT CODE CARRIES THE VERDICT (family law: a gate 40// whose exit code does not carry its verdict silently blesses every failure it finds). 41// license_tier: ORIGINAL expect_exit: 0 42import "nx_uiq_color.nx" 43const K_MAGIC_2097152: i64 = 2097152 44const RSP_REFLOW_PX: i64 = 320 // WCAG 2.1 SC 1.4.10 Reflow bar 45const RSP_TAP_MIN_PX: i64 = 24 // WCAG 2.2 SC 2.5.8 Target Size (Minimum), AA 46 47// ---------- tiny scanners ---------- 48 49// literal search from `from`; byte offset or -1. 50func rsp_find_from(buf: *u8, n: i64, pat: *u8, from: i64) -> i64 { 51 var pl: i64=0 52 while pat[pl]!=(0 as u8) { pl=pl+1 } 53 if pl==0 { return 0-1 } 54 var i: i64=from 55 if i<0 { i=0 } 56 while i+pl<=n { 57 var j: i64=0 58 var ok: i64=1 59 while j<pl { if buf[i+j]!=pat[j] { ok=0; j=pl } else { j=j+1 } } 60 if ok==1 { return i } 61 i=i+1 62 } 63 return 0-1 64} 65 66// count occurrences of a literal. 67func rsp_count(buf: *u8, n: i64, pat: *u8) -> i64 { 68 var pl: i64=0 69 while pat[pl]!=(0 as u8) { pl=pl+1 } 70 if pl==0 { return 0 } 71 var c: i64=0 72 var i: i64 = rsp_find_from(buf,n,pat,0) 73 while i>=0 { c=c+1; i=rsp_find_from(buf,n,pat,i+pl) } 74 return c 75} 76 77// skip spaces only. 78func rsp_sp(buf: *u8, n: i64, i: i64) -> i64 { 79 var j: i64=i 80 var run: i64=1 81 while run==1 { 82 if j<n { if buf[j]==(32 as u8) { j=j+1 } else { run=0 } } else { run=0 } 83 } 84 return j 85} 86// skip spaces and commas (track-list separators). 87func rsp_sepc(buf: *u8, n: i64, i: i64) -> i64 { 88 var j: i64=i 89 var run: i64=1 90 while run==1 { 91 if j<n { 92 let c: i64=buf[j] as i64 93 if c==32 { j=j+1 } else { if c==44 { j=j+1 } else { run=0 } } 94 } else { run=0 } 95 } 96 return j 97} 98// skip an alphabetic unit run (px, em, rem, ...). 99func rsp_unit(buf: *u8, n: i64, i: i64) -> i64 { 100 var j: i64=i 101 var run: i64=1 102 while run==1 { 103 if j<n { 104 let c: i64=buf[j] as i64 105 var al: i64=0 106 if c>=97 { if c<=122 { al=1 } } 107 if c>=65 { if c<=90 { al=1 } } 108 if al==1 { j=j+1 } else { run=0 } 109 } else { run=0 } 110 } 111 return j 112} 113 114// parse a decimal at i. returns value or -1 when no digit is present. end index lands in pend[0]. 115// NOTE: a separate `pend` out-slot rather than clobbering the cursor -- family law: a loop that breaks 116// by clobbering its own cursor cannot also report where it stopped. 117func rsp_num(buf: *u8, n: i64, i: i64, pend: *i64) -> i64 { 118 var v: i64=0 119 var j: i64=i 120 var any: i64=0 121 var run: i64=1 122 while run==1 { 123 if j<n { 124 let c: i64=buf[j] as i64 125 if c>=48 { 126 if c<=57 { v=v*10+(c-48); j=j+1; any=1 } else { run=0 } 127 } else { run=0 } 128 } else { run=0 } 129 } 130 pend[0]=j 131 if any==0 { return 0-1 } 132 return v 133} 134 135// enclosing declaration block for a byte offset: first '{' at/behind i, and first '}' at/after i. 136func rsp_blk_start(buf: *u8, i: i64) -> i64 { 137 var j: i64=i 138 var run: i64=1 139 while run==1 { 140 if j>0 { if buf[j]==(123 as u8) { run=0 } else { j=j-1 } } else { run=0 } 141 } 142 return j 143} 144func rsp_blk_end(buf: *u8, n: i64, i: i64) -> i64 { 145 var j: i64=i 146 var run: i64=1 147 while run==1 { 148 if j<n { if buf[j]==(125 as u8) { run=0 } else { j=j+1 } } else { run=0 } 149 } 150 return j 151} 152 153// FIRST px value of `prop` inside [a,b). -1 = absent. 154func rsp_prop_px(buf: *u8, a: i64, b: i64, prop: *u8) -> i64 { 155 var pl: i64=0 156 while prop[pl]!=(0 as u8) { pl=pl+1 } 157 if pl==0 { return 0-1 } 158 let pe: *i64 = sys_mmap(16) as *i64 159 var found: i64=0-1 160 var i: i64=a 161 var stop: i64=0 162 while stop==0 { 163 if i+pl>b { stop=1 } else { 164 var j: i64=0 165 var ok: i64=1 166 while j<pl { if buf[i+j]!=prop[j] { ok=0; j=pl } else { j=j+1 } } 167 if ok==1 { 168 var k: i64 = rsp_sp(buf,b,i+pl) 169 if k<b { 170 if buf[k]==(58 as u8) { 171 k = rsp_sp(buf,b,k+1) 172 let v: i64 = rsp_num(buf,b,k,pe) 173 if v>=0 { found=v; stop=1 } else { i=i+1 } 174 } else { i=i+1 } 175 } else { i=i+1 } 176 } else { i=i+1 } 177 } 178 } 179 return found 180} 181 182// HORIZONTAL px value of a 1-or-2-value shorthand inside [a,b): `padding:8px` -> 8 ; 183// `padding:10px 0` -> 0 ; `padding:0 20px` -> 20. CSS shorthand semantics: 1 value = all sides, 184// 2 values = vertical horizontal. -1 = absent. 185func rsp_prop_px_h(buf: *u8, a: i64, b: i64, prop: *u8) -> i64 { 186 var pl: i64=0 187 while prop[pl]!=(0 as u8) { pl=pl+1 } 188 if pl==0 { return 0-1 } 189 let pe: *i64 = sys_mmap(16) as *i64 190 var found: i64=0-1 191 var i: i64=a 192 var stop: i64=0 193 while stop==0 { 194 if i+pl>b { stop=1 } else { 195 var j: i64=0 196 var ok: i64=1 197 while j<pl { if buf[i+j]!=prop[j] { ok=0; j=pl } else { j=j+1 } } 198 if ok==1 { 199 var k: i64 = rsp_sp(buf,b,i+pl) 200 if k<b { 201 if buf[k]==(58 as u8) { 202 k = rsp_sp(buf,b,k+1) 203 let v1: i64 = rsp_num(buf,b,k,pe) 204 if v1>=0 { 205 var m: i64 = rsp_unit(buf,b,pe[0]) 206 m = rsp_sp(buf,b,m) 207 let v2: i64 = rsp_num(buf,b,m,pe) 208 if v2>=0 { found=v2 } else { found=v1 } 209 stop=1 210 } else { i=i+1 } 211 } else { i=i+1 } 212 } else { i=i+1 } 213 } else { i=i+1 } 214 } 215 } 216 return found 217} 218 219// largest horizontal container padding declared anywhere (REPORTED, never gated -- see the 220// DECLARED IMPRECISION note in the header). 221func rsp_container_pad(buf: *u8, n: i64) -> i64 { 222 var worst: i64=0 223 var i: i64 = rsp_find_from(buf,n,"padding" as *u8,0) 224 while i>=0 { 225 let a: i64 = rsp_blk_start(buf,i) 226 let b: i64 = rsp_blk_end(buf,n,i) 227 let p: i64 = rsp_prop_px_h(buf,a,b,"padding" as *u8) 228 if p>worst { worst=p } 229 i = rsp_find_from(buf,n,"padding" as *u8,i+7) 230 } 231 return worst 232} 233 234// ---------- the reflow measurement ---------- 235// res[0]=fixed_grids res[1]=worst_min_px res[2]=reflow_fails res[3]=widest_cols 236func rsp_grids(buf: *u8, n: i64, res: *i64) -> i64 { 237 let pe: *i64 = sys_mmap(16) as *i64 238 var grids: i64=0 239 var worst: i64=0 240 var fails: i64=0 241 var wcols: i64=0 242 var i: i64 = rsp_find_from(buf,n,"repeat(" as *u8,0) 243 while i>=0 { 244 var k: i64 = rsp_sepc(buf,n,i+7) 245 let cnt: i64 = rsp_num(buf,n,k,pe) 246 if cnt>0 { 247 k = rsp_sepc(buf,n,pe[0]) 248 let w: i64 = rsp_num(buf,n,k,pe) 249 if w>0 { 250 // the track width must be an absolute px length; anything else is not a fixed track. 251 var ispx: i64=0 252 let q: i64 = pe[0] 253 if q+1<n { if buf[q]==(112 as u8) { if buf[q+1]==(120 as u8) { ispx=1 } } } 254 if ispx==1 { 255 let a: i64 = rsp_blk_start(buf,i) 256 let b: i64 = rsp_blk_end(buf,n,i) 257 var gap: i64 = rsp_prop_px(buf,a,b,"gap" as *u8) 258 if gap<0 { gap=0 } 259 var pad: i64 = rsp_prop_px_h(buf,a,b,"padding" as *u8) 260 if pad<0 { pad=0 } 261 let mw: i64 = cnt*w + (cnt-1)*gap + 2*pad 262 grids=grids+1 263 if mw>worst { worst=mw; wcols=cnt } 264 if mw>RSP_REFLOW_PX { fails=fails+1 } 265 } 266 } 267 } 268 i = rsp_find_from(buf,n,"repeat(" as *u8,i+7) 269 } 270 res[0]=grids; res[1]=worst; res[2]=fails; res[3]=wcols 271 return fails 272} 273 274// ---------- tap targets ---------- 275// smallest declared pointer-target edge, or -1 when none is DECLARED (UNKNOWN, never a pass). 276func rsp_tap_min(buf: *u8, n: i64) -> i64 { 277 var smallest: i64=0-1 278 var i: i64 = rsp_find_from(buf,n,"cursor" as *u8,0) 279 while i>=0 { 280 let a: i64 = rsp_blk_start(buf,i) 281 let b: i64 = rsp_blk_end(buf,n,i) 282 if rsp_find_from(buf,b,"pointer" as *u8,a)>=0 { 283 let w: i64 = rsp_prop_px(buf,a,b,"width" as *u8) 284 let h: i64 = rsp_prop_px(buf,a,b,"height" as *u8) 285 var e: i64 = 0-1 286 if w>=0 { e=w } 287 if h>=0 { if e<0 { e=h } else { if h<e { e=h } } } 288 if e>=0 { if smallest<0 { smallest=e } else { if e<smallest { smallest=e } } } 289 } 290 i = rsp_find_from(buf,n,"cursor" as *u8,i+6) 291 } 292 return smallest 293} 294 295// ---------- zoom / viewport ---------- 296// bit0 = viewport meta declares width=device-width ; bit1 = zoom is suppressed (SC 1.4.4 fail) 297func rsp_zoom(buf: *u8, n: i64) -> i64 { 298 var f: i64=0 299 if rsp_find_from(buf,n,"width=device-width" as *u8,0)>=0 { f=f+1 } 300 if rsp_find_from(buf,n,"user-scalable=no" as *u8,0)>=0 { f=f+2 } 301 if rsp_find_from(buf,n,"maximum-scale=1" as *u8,0)>=0 { if ((f/2) & 1)==0 { f=f+2 } } 302 return f 303} 304 305// ---------- audit ---------- 306// emit=1 -> JSON. returns total issues (0 = GREEN). 307func rsp_audit(buf: *u8, n: i64, emit: i64) -> i64 { 308 let res: *i64 = sys_mmap(64) as *i64 309 rsp_grids(buf,n,res) 310 let grids: i64=res[0] 311 let worst: i64=res[1] 312 let reflow: i64=res[2] 313 let wcols: i64=res[3] 314 let tap: i64 = rsp_tap_min(buf,n) 315 let zf: i64 = rsp_zoom(buf,n) 316 let vp: i64 = zf & 1 317 let nozoom: i64 = (zf/2) & 1 318 let media: i64 = rsp_count(buf,n,"@media" as *u8) 319 let cpad: i64 = rsp_container_pad(buf,n) 320 321 var tapfail: i64=0 322 if tap>=0 { if tap<RSP_TAP_MIN_PX { tapfail=1 } } 323 var vpfail: i64=0 324 if vp==0 { vpfail=1 } 325 // ADAPTIVE: fixed-track grids with no media query at all cannot adapt. Bound to its denominator 326 // (family law: a tooth that passes on the empty set is not a tooth) -- only asserted when grids>0. 327 var adapt: i64=0 328 if grids>0 { if media==0 { adapt=1 } } 329 330 let issues: i64 = reflow + tapfail + nozoom + vpfail + adapt 331 332 if emit==1 { 333 uiq_w("{\"bar\":\"WCAG 2.1 SC 1.4.10 Reflow @ " as *u8); uiq_pn(RSP_REFLOW_PX); uiq_w("px\"" as *u8) 334 uiq_w(",\"bytes\":" as *u8); uiq_pn(n) 335 uiq_w(",\"fixed_grids\":" as *u8); uiq_pn(grids) 336 uiq_w(",\"worst_grid_min_px\":" as *u8); uiq_pn(worst) 337 uiq_w(",\"worst_grid_cols\":" as *u8); uiq_pn(wcols) 338 uiq_w(",\"reflow_fails\":" as *u8); uiq_pn(reflow) 339 uiq_w(",\"container_pad_max_px\":" as *u8); uiq_pn(cpad) 340 uiq_w(",\"effective_budget_px\":" as *u8); uiq_pn(RSP_REFLOW_PX-cpad) 341 uiq_w(",\"tap_min_px\":" as *u8) 342 if tap<0 { uiq_w("\"UNKNOWN\"" as *u8) } else { uiq_pn(tap) } 343 uiq_w(",\"tap_fail\":" as *u8); uiq_pn(tapfail) 344 uiq_w(",\"viewport_meta_ok\":" as *u8); uiq_pn(vp) 345 uiq_w(",\"zoom_suppressed\":" as *u8); uiq_pn(nozoom) 346 uiq_w(",\"media_queries\":" as *u8); uiq_pn(media) 347 uiq_w(",\"no_adaptive_rule\":" as *u8); uiq_pn(adapt) 348 uiq_w(",\"issues\":" as *u8); uiq_pn(issues) 349 uiq_w(",\"verdict\":\"" as *u8) 350 if issues==0 { uiq_w("GREEN" as *u8) } else { uiq_w("RED" as *u8) } 351 uiq_w("\"}" as *u8) 352 let nl: *u8 = sys_mmap(2); nl[0]=10 as u8 353 sys_write(1,nl,1) 354 } 355 return issues 356} 357 358func rsp_len(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8) { n=n+1 } return n } 359 360// ---------- self-test: BOTH DIRECTIONS, and it asserts the ARITHMETIC not just the branch ---------- 361// Family law: a green that never had a corresponding red is unverified; and a deny-guard ships with a 362// POSITIVE control (an input that MUST be allowed) or a guard that refuses everything passes every 363// negative test. 364func rsp_selftest() -> i64 { 365 var f: i64=0 366 let res: *i64 = sys_mmap(64) as *i64 367 368 // (1) NEG-CONTROL / THE REAL DEFECT: the live Connect Four board, verbatim shape. 369 // 7*46 + 6*5 + 2*8 = 368. Assert the NUMBER -- proving the branch fired is not proving the maths. 370 let c4: *u8 = "#c4{display:inline-grid;grid-template-columns:repeat(7,46px);grid-gap:5px;background:#0e2a4a;padding:8px;border-radius:10px;margin:10px 0}" as *u8 371 rsp_grids(c4, rsp_len(c4), res) 372 if res[0]!=1 { f=f+1 } // exactly one fixed grid found 373 if res[1]!=368 { f=f+1 } // the arithmetic itself 374 if res[2]!=1 { f=f+1 } // and it fails the 320px bar 375 if res[3]!=7 { f=f+1 } // 7 columns 376 377 // (2) POSITIVE CONTROL: the live Tic-Tac-Toe board FITS -- 3*72 + 2*6 = 228 <= 320. 378 // Without this, a ruler that flagged every fixed grid would score 100% on (1). 379 let ttt: *u8 = "#ttt{display:grid;grid-template-columns:repeat(3,72px);grid-gap:6px;margin:10px 0}" as *u8 380 rsp_grids(ttt, rsp_len(ttt), res) 381 if res[0]!=1 { f=f+1 } 382 if res[1]!=228 { f=f+1 } 383 if res[2]!=0 { f=f+1 } // must NOT fire 384 385 // (3) auto-fill/minmax is genuinely responsive -> must parse as NOT a fixed grid (no false positive). 386 let af: *u8 = ".grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));grid-gap:12px}" as *u8 387 rsp_grids(af, rsp_len(af), res) 388 if res[0]!=0 { f=f+1 } 389 if res[2]!=0 { f=f+1 } 390 391 // (4) ANTI-VACUITY: no grid at all -> zero found AND no reflow claim. Bounds the tooth to its 392 // denominator so it cannot "pass" on an empty population. 393 let ng: *u8 = "body{margin:0;color:#111}" as *u8 394 rsp_grids(ng, rsp_len(ng), res) 395 if res[0]!=0 { f=f+1 } 396 if res[2]!=0 { f=f+1 } 397 398 // (5) shorthand horizontal padding semantics: 1 value = all, 2 values = vertical horizontal. 399 let p1: *u8 = "x{padding:8px}" as *u8 400 if rsp_prop_px_h(p1,0,rsp_len(p1),"padding" as *u8)!=8 { f=f+1 } 401 let p2: *u8 = "x{padding:0 20px}" as *u8 402 if rsp_prop_px_h(p2,0,rsp_len(p2),"padding" as *u8)!=20 { f=f+1 } 403 let p3: *u8 = "x{padding:10px 0}" as *u8 404 if rsp_prop_px_h(p3,0,rsp_len(p3),"padding" as *u8)!=0 { f=f+1 } 405 406 // (6) zoom suppression (SC 1.4.4) is caught, and a clean viewport is not falsely accused. 407 let z1: *u8 = "<meta name='viewport' content='width=device-width,initial-scale=1,user-scalable=no'>" as *u8 408 let zr1: i64 = rsp_zoom(z1,rsp_len(z1)) 409 if (zr1 & 1)!=1 { f=f+1 } 410 if ((zr1/2) & 1)!=1 { f=f+1 } 411 let z2: *u8 = "<meta name='viewport' content='width=device-width,initial-scale=1'>" as *u8 412 let zr2: i64 = rsp_zoom(z2,rsp_len(z2)) 413 if (zr2 & 1)!=1 { f=f+1 } 414 if ((zr2/2) & 1)!=0 { f=f+1 } 415 416 // (7) tap target: an UNDECLARED size must report UNKNOWN (-1), never a pass. 417 let t0: *u8 = "a{color:#111}" as *u8 418 if rsp_tap_min(t0,rsp_len(t0))!=(0-1) { f=f+1 } 419 let t1: *u8 = ".d{width:18px;height:18px;cursor:pointer}" as *u8 420 if rsp_tap_min(t1,rsp_len(t1))!=18 { f=f+1 } 421 422 // (8) END-TO-END both directions on whole-page fixtures. 423 // RED: fixed 368px board, no media query. 424 let bad: *u8 = "<meta name='viewport' content='width=device-width,initial-scale=1'><style>#c4{display:inline-grid;grid-template-columns:repeat(7,46px);grid-gap:5px;padding:8px}</style>" as *u8 425 if rsp_audit(bad,rsp_len(bad),0)<1 { f=f+1 } 426 // GREEN: fluid grid + a media query + a compliant viewport. 427 let good: *u8 = "<meta name='viewport' content='width=device-width,initial-scale=1'><style>.g{display:grid;grid-template-columns:repeat(auto-fill,minmax(90px,1fr));gap:8px}@media(max-width:640px){.g{gap:6px}}</style>" as *u8 428 if rsp_audit(good,rsp_len(good),0)!=0 { f=f+1 } 429 return f 430} 431 432func rsp_slurp(path: *u8, buf: *u8, cap: i64) -> i64 { 433 let fd: i64 = sys_openat_rd(path) 434 if fd<0 { return 0 } 435 var total: i64=0 436 var nrd: i64=sys_read(fd, buf, cap) 437 while nrd>0 { 438 total=total+nrd 439 if total>=cap { nrd=0 } else { nrd=sys_read(fd, ((buf as i64)+total) as *u8, cap-total) } 440 } 441 sys_close(fd) 442 return total 443} 444 445func main(argc: i64, argv: *i64) -> i64 { 446 if argc>=2 { 447 let a1: *u8 = argv[1] as *u8 448 if a1[0]==(45 as u8) { 449 uiq_w("=== nx_uiq_responsive --kat (WCAG 2.1 SC 1.4.10 Reflow @ 320px) ===\n" as *u8) 450 let f: i64 = rsp_selftest() 451 uiq_w("checks failed=" as *u8); uiq_pn(f) 452 uiq_w(" (c4-368-neg-control + ttt-228-pos-control + autofill-no-false-positive + anti-vacuity + shorthand + zoom + tap + e2e-both-directions)\n" as *u8) 453 if f==0 { uiq_w("VERDICT=GREEN\n" as *u8); sys_exit(0); return 0 } 454 uiq_w("VERDICT=RED\n" as *u8); sys_exit(1); return 1 455 } 456 let cap: i64 = K_MAGIC_2097152 457 let buf: *u8 = sys_mmap(cap) 458 let n: i64 = rsp_slurp(a1, buf, cap) 459 if n<=0 { uiq_w("{\"error\":\"cannot read page\"}\n" as *u8); sys_exit(2); return 2 } 460 let issues: i64 = rsp_audit(buf, n, 1) 461 if issues==0 { sys_exit(0); return 0 } 462 sys_exit(1); return 1 463 } 464 uiq_w("usage: nx_uiq_responsive --kat | <html-file-path>\n" as *u8) 465 sys_exit(2); return 2 466}