code wiki / (root) / nx_chat_unicode_store_t60.nx

nx_chat_unicode_store_t60.nx source

↩ module page · 458 lines · 19804 B

1// nx_chat_demo_api.nx -- the SAFE public front for a testable persistent-chat UI backed by C1's store. 2// v2 (2026-08-16): does the append/fetch IN-PROCESS on the seg-store, NOT by forking nx_chat_store. 3// WHY: MEASURED via curl to /mcp -- a list (read) returned in 44ms, but a post that FORKED nx_chat_store 4// to write hung to the tools-daemon exec deadline (~14s) and returned empty (exit -5), because the 5// write-path grandchild orphaned under the daemon's capture. A read fork was fine; a write fork was not. 6// In-process removes the fork entirely: this organ takes the plane lock, appends, and releases in its own 7// process -- the exact code path nx_chat_store's gate proves, just linked instead of exec'd. Fast + no orphan. 8// SECURITY BOUNDARY unchanged: only post/list, rooms forced into demo_, inputs sanitized + capped, budgets 9// from C1's conf refuse loudly. Destructive verbs do not exist here. REFEREE: nx_chat_demo_api_gate. 10// DEBT (named, not a silent twin): the append/fetch logic is duplicated from nx_chat_store; the consolidation 11// is to extract nx_chat_store_lib that BOTH this front and the CLI import (comms.plan chatstore-lib-extract). 12// license_tier: ORIGINAL No hw writes (Rule 26). 13import "nx_syscalls.nx" 14import "nx_itoa_lib.nx" 15import "nx_store_seed_lib.nx" 16import "nx_seg_store.nx" 17import "nx_utf8.nx" 18const CDA_TEXT_BAD_BOUNDARY:i64=-1 19const CDA_TEXT_TOO_LONG:i64=-2 20const CDA_TEXT_BAD_UTF8:i64=-3 21 22const CDA_EXIT_OK: i64 = 0 23const CDA_EXIT_USAGE: i64 = 2 24const CDA_EXIT_REFUSED: i64 = 3 25const CDA_ROOM_MAX: i64 = 24 26const CDA_NAME_MAX: i64 = 24 27const CDA_TEXT_MAX: i64 = 280 28const CDA_FETCH_MAX: i64 = 200 29const CDA_PFX_CAP: i64 = 256 30const CDA_ROW_OVH: i64 = 128 31const CDA_WSLACK: i64 = 65536 32const CDA_CONF_CAP: i64 = 192 33const CDA_OUTCAP: i64 = 262144 34const CDA_NL: i64 = 10 35const CDA_PIPE: i64 = 124 36const CDA_KIND_TEXT: i64 = 1 37const CDA_KIND_SYS: i64 = 4 38const CHATCONF: *u8 = "knowledge/chat-unicode-store-t60.conf" 39const CSA_ERR_CONF: i64 = 0 - 3 40const CSA_ERR_BODY: i64 = 0 - 10 41const CSA_ERR_LOCK: i64 = 0 - 4 42const CSA_ERR_MSGS: i64 = 0 - 11 43const CSA_ERR_BYTES: i64 = 0 - 12 44const CSA_ERR_CORRUPT: i64 = 0 - 5 45 46func cda_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 47func cda_wn(s: *u8, n: i64) -> i64 { sys_write(1, s, n); return 0 } 48func cda_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 49func cda_eq(a: *u8, b: *u8) -> i64 { 50 var i: i64 = 0 51 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 52 if b[i] != (0 as u8) { return 0 } 53 return 1 54} 55func cda_ident(src: *u8, dst: *u8, cap: i64) -> i64 { 56 var o: i64 = 0 57 var i: i64 = 0 58 while src[i] != (0 as u8) { 59 if o >= cap { break } 60 let c: i64 = src[i] as i64 61 if c >= 48 { if c <= 57 { dst[o] = c as u8; o = o + 1 } } 62 if c >= 97 { if c <= 122 { dst[o] = c as u8; o = o + 1 } } 63 if c >= 65 { if c <= 90 { dst[o] = (c + 32) as u8; o = o + 1 } } 64 if c == 95 { dst[o] = 95 as u8; o = o + 1 } 65 i = i + 1 66 } 67 dst[o] = 0 as u8 68 return o 69} 70// cap is payload bytes; callers reserve one additional terminator byte. 71func cda_text_legacy(src: *u8, dst: *u8, cap: i64) -> i64 { 72 var o: i64 = 0 73 var i: i64 = 0 74 while src[i] != (0 as u8) { 75 if o >= cap { break } 76 let c: i64 = src[i] as i64 77 if c >= 32 { if c <= 126 { dst[o] = c as u8; o = o + 1 } } 78 i = i + 1 79 } 80 dst[o] = 0 as u8 81 return o 82} 83func cda_text(src: *u8, dst: *u8, cap: i64) -> i64 { 84 if cap < 0 { return CDA_TEXT_BAD_BOUNDARY } 85 var n:i64=0 86 while n<=cap { 87 if src[n]==(0 as u8) { break } 88 n=n+1 89 } 90 if n>cap { dst[0]=0 as u8;return CDA_TEXT_TOO_LONG } 91 if utf8_validate(src,n)!=n { dst[0]=0 as u8;return CDA_TEXT_BAD_UTF8 } 92 var i:i64=0 93 while i<n { dst[i]=src[i];i=i+1 } 94 dst[n]=0 as u8 95 return n 96} 97func cda_demoroom(clean: *u8, out: *u8) -> i64 { 98 var o: i64 = 0 99 let pre: *u8 = "demo_" as *u8 100 while pre[o] != (0 as u8) { out[o] = pre[o]; o = o + 1 } 101 var i: i64 = 0 102 while clean[i] != (0 as u8) { out[o] = clean[i]; o = o + 1; i = i + 1 } 103 out[o] = 0 as u8 104 return o 105} 106func cda_jesc(out: *u8, o: i64, s: *u8, n: i64) -> i64 { 107 var i: i64 = 0 108 while i < n { 109 let c: i64 = s[i] as i64 110 if c == 34 { out[o] = 92 as u8; o = o + 1; out[o] = 34 as u8; o = o + 1 } else { 111 if c == 92 { out[o] = 92 as u8; o = o + 1; out[o] = 92 as u8; o = o + 1 } else { 112 if c < 32 { out[o]=92 as u8;out[o+1]=117 as u8;out[o+2]=48 as u8;out[o+3]=48 as u8;o=o+4;o=o+cda_hexe((s as i64+i) as *u8,1,(out as i64+o) as *u8) } else { 113 out[o] = c as u8; o = o + 1 114 } } } 115 i = i + 1 116 } 117 return o 118} 119func cda_hexe(src: *u8, n: i64, dst: *u8) -> i64 { 120 var i: i64 = 0 121 while i < n { 122 let b: i64 = src[i] as i64 123 let hi: i64 = b / 16 124 let lo: i64 = b % 16 125 if hi < 10 { dst[i*2] = (48 + hi) as u8 } else { dst[i*2] = (87 + hi) as u8 } 126 if lo < 10 { dst[i*2+1] = (48 + lo) as u8 } else { dst[i*2+1] = (87 + lo) as u8 } 127 i = i + 1 128 } 129 return n * 2 130} 131func cda_hexv(c: i64) -> i64 { 132 if c >= 48 { if c <= 57 { return c - 48 } } 133 if c >= 97 { if c <= 102 { return c - 87 } } 134 return 0 - 1 135} 136func cda_catn(d: *u8, o: i64, v: i64) -> i64 { 137 if v == 0 { d[o] = 48 as u8; return o + 1 } 138 let tmp: *u8 = sys_mmap(24) 139 var k: i64 = 0 140 var m: i64 = v 141 while m > 0 { tmp[k] = (48 + m % 10) as u8; m = m / 10; k = k + 1 } 142 var j: i64 = k - 1 143 while j >= 0 { d[o] = tmp[j]; o = o + 1; j = j - 1 } 144 return o 145} 146func cda_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { d[o] = s[i]; o = o + 1; i = i + 1 } return o } 147func cda_confs(key: *u8, out: *u8, cap: i64) -> i64 { 148 let ol: *i64 = sts_mm(16) as *i64 149 let b: *u8 = sys_read_file(CHATCONF, ol) 150 let n: i64 = ol[0] 151 if b as i64 == 0 { return 0 - 1 } 152 if n <= 0 { return 0 - 1 } 153 let kl: i64 = cda_len(key) 154 var i: i64 = 0 155 while i < n { 156 var j: i64 = 0 157 var hit: i64 = 1 158 while j < kl { 159 if i + j >= n { hit = 0; break } 160 if b[i+j] != key[j] { hit = 0; break } 161 j = j + 1 162 } 163 if hit == 1 { if i + kl >= n { hit = 0 } else { if b[i+kl] != (CDA_PIPE as u8) { hit = 0 } } } 164 if hit == 1 { 165 var v: i64 = i + kl + 1 166 var o: i64 = 0 167 while v < n { if b[v] == (CDA_NL as u8) { break } if o < cap - 1 { out[o] = b[v]; o = o + 1 } v = v + 1 } 168 out[o] = 0 as u8 169 return o 170 } 171 while i < n { if b[i] == (CDA_NL as u8) { break } i = i + 1 } 172 i = i + 1 173 } 174 return 0 - 1 175} 176func cda_confn(key: *u8) -> i64 { 177 let v: *u8 = sts_mm(CDA_CONF_CAP) 178 let l: i64 = cda_confs(key, v, CDA_CONF_CAP) 179 if l <= 0 { return 0 - 1 } 180 return sts_atoi(v, l) 181} 182func cda_prefix(room: *u8, out: *u8) -> i64 { 183 let rl: i64 = cda_confs("store_root" as *u8, out, CDA_PFX_CAP - 96) 184 if rl <= 0 { return 0 - 2 } 185 var o: i64 = rl 186 o = ss_cat(out, o, "chat_" as *u8) 187 o = ss_cat(out, o, room) 188 o = ss_cat(out, o, "-" as *u8) 189 out[o] = 0 as u8 190 return o 191} 192func cda_rowbuild(seq: i64, kind: i64, sender: *u8, body: *u8, blen: i64, out: *u8) -> i64 { 193 var o: i64 = ss_cat(out, 0, "m|" as *u8) 194 o = ss_catn(out, o, seq) 195 o = ss_cat(out, o, "|" as *u8) 196 o = ss_catn(out, o, sys_now_realtime_ms()) 197 o = ss_cat(out, o, "|" as *u8) 198 o = ss_catn(out, o, kind) 199 o = ss_cat(out, o, "|" as *u8) 200 o = ss_cat(out, o, sender) 201 o = ss_cat(out, o, "|" as *u8) 202 o = o + cda_hexe(body, blen, (out as i64 + o) as *u8) 203 out[o] = 0 as u8 204 return o 205} 206func cda_delkey(seq: i64, out: *u8) -> i64 { 207 var o: i64 = ss_cat(out, 0, "del:" as *u8) 208 o = ss_catn(out, o, seq) 209 out[o] = 0 as u8 210 return o 211} 212func cda_put_key(prefix: *u8, key: *u8, val: *u8, vlen: i64) -> i64 { 213 let w: *i64 = ss_begin_cap(vlen + CDA_WSLACK) 214 if ss_add(w, 1, key, val, vlen) < 0 { return 0 - 1 } 215 if ss_commit_cas(prefix, w, ss_next_segid(prefix), SS_CAS_ANY) != 0 { return 0 - 1 } 216 return 0 217} 218func csa_append(room: *u8, sender: *u8, body: *u8, blen: i64) -> i64 { 219 let bmax: i64 = cda_confn("body_bytes_max" as *u8) 220 if bmax < 0 { return CSA_ERR_CONF } 221 let bcliff: i64 = cda_confn("room_bytes_cliff" as *u8) 222 if bcliff < 0 { return CSA_ERR_CONF } 223 let mcliff: i64 = cda_confn("room_msgs_cliff" as *u8) 224 if mcliff < 0 { return CSA_ERR_CONF } 225 if blen > bmax { return CSA_ERR_BODY } 226 let pfx: *u8 = sts_mm(CDA_PFX_CAP) 227 if cda_prefix(room, pfx) < 0 { return CSA_ERR_CONF } 228 let lk: i64 = sts_lock(pfx) 229 if lk < 0 { return CSA_ERR_LOCK } 230 let pq: *i64 = sts_mm(16) as *i64 231 let lq: *i64 = sts_mm(16) as *i64 232 var n: i64 = 0 233 if ss_get(pfx, "q:n" as *u8, pq, lq) == 1 { 234 n = sts_atoi(pq[0] as *u8, lq[0]) 235 } else { 236 // auto-open with a DEFERRED commit too (no fsync): write q:0=sysrow, q:n=1, meta:bytes in one 237 // segment. The old path used sts_seed which fsyncs, and under disk pressure that stalled past the 238 // caller's deadline BEFORE the room was created, so every retry re-stalled. Deferred fixes it. 239 let ob: *u8 = "room-open" as *u8 240 let obl: i64 = 9 241 let srow: *u8 = sts_mm(CDA_ROW_OVH + 64 + obl * 2 + 8) 242 let so: i64 = cda_rowbuild(1, CDA_KIND_SYS, sender, ob, obl, srow) 243 let k0: *u8 = sts_mm(64) 244 sts_rowkey(0, k0) 245 let c1: *u8 = sts_mm(32) 246 let cl1: i64 = ss_catn(c1, 0, 1) 247 let m0: *u8 = sts_mm(32) 248 let ml0: i64 = ss_catn(m0, 0, obl) 249 let ow: *i64 = ss_begin_cap(so + CDA_WSLACK) 250 if ss_add(ow, 1, k0, srow, so) < 0 { sts_unlock(lk) return CSA_ERR_CORRUPT } 251 if ss_add(ow, 1, "q:n" as *u8, c1, cl1) < 0 { sts_unlock(lk) return CSA_ERR_CORRUPT } 252 if ss_add(ow, 1, "meta:bytes" as *u8, m0, ml0) < 0 { sts_unlock(lk) return CSA_ERR_CORRUPT } 253 if ss_commit_deferred(pfx, ow, ss_next_segid(pfx)) != 0 { sts_unlock(lk) return CSA_ERR_CORRUPT } 254 n = 1 255 } 256 if n >= mcliff { sts_unlock(lk) return CSA_ERR_MSGS } 257 var bts: i64 = 0 258 if ss_get(pfx, "meta:bytes" as *u8, pq, lq) == 1 { bts = sts_atoi(pq[0] as *u8, lq[0]) } 259 if bts + blen > bcliff { sts_unlock(lk) return CSA_ERR_BYTES } 260 let seq: i64 = n + 1 261 let row: *u8 = sts_mm(CDA_ROW_OVH + 64 + blen * 2 + 8) 262 let rl: i64 = cda_rowbuild(seq, CDA_KIND_TEXT, sender, body, blen, row) 263 let key: *u8 = sts_mm(64) 264 sts_rowkey(n, key) 265 let cb: *u8 = sts_mm(32) 266 let cl: i64 = ss_catn(cb, 0, seq) 267 let mb: *u8 = sts_mm(32) 268 let ml: i64 = ss_catn(mb, 0, bts + blen) 269 let w: *i64 = ss_begin_cap(rl + CDA_WSLACK) 270 if ss_add(w, 1, key, row, rl) < 0 { sts_unlock(lk) return CSA_ERR_CORRUPT } 271 if ss_add(w, 1, "q:n" as *u8, cb, cl) < 0 { sts_unlock(lk) return CSA_ERR_CORRUPT } 272 if ss_add(w, 1, "meta:bytes" as *u8, mb, ml) < 0 { sts_unlock(lk) return CSA_ERR_CORRUPT } 273 // DEFERRED COMMIT (perf): write the segment + manifest to the page cache and return -- the message is 274 // visible to the next read immediately and survives process death; the fsync to disk happens on the 275 // store's background sync. We hold the plane flock across the whole read-modify-write, so no CAS is 276 // needed (nothing else can commit between our ss_get and this commit). This keeps a post FAST even 277 // when the box's disk is under pressure, instead of stalling on a synchronous fsync. 278 let rc: i64 = ss_commit_deferred(pfx, w, ss_next_segid(pfx)) 279 sts_unlock(lk) 280 if rc != 0 { return CSA_ERR_CORRUPT } 281 return seq 282} 283func csa_fetch(room: *u8, since: i64, maxn: i64, outbuf: *u8, outcap: i64) -> i64 { 284 let pfx: *u8 = sts_mm(CDA_PFX_CAP) 285 if cda_prefix(room, pfx) < 0 { return 0 - 1 } 286 let pq: *i64 = sts_mm(16) as *i64 287 let lq: *i64 = sts_mm(16) as *i64 288 if ss_get(pfx, "q:n" as *u8, pq, lq) != 1 { return 0 - 1 } 289 let n: i64 = sts_atoi(pq[0] as *u8, lq[0]) 290 let h: *i64 = ss_open_cached(pfx) 291 if h as i64 == 0 { return 0 - 5 } 292 let key: *u8 = sts_mm(64) 293 let dk: *u8 = sts_mm(64) 294 let pr2: *i64 = sts_mm(16) as *i64 295 let lr2: *i64 = sts_mm(16) as *i64 296 var o: i64 = 0 297 var returned: i64 = 0 298 var i: i64 = since 299 while i < n { 300 if returned >= maxn { break } 301 sts_rowkey(i, key) 302 if ss_hget(h, key, pq, lq) != 1 { return 0 - 5 } 303 let rv: *u8 = pq[0] as *u8 304 let rl: i64 = lq[0] 305 cda_delkey(i + 1, dk) 306 if ss_hget(h, dk, pr2, lr2) == 1 { } else { 307 if o + rl + 1 < outcap { 308 var k: i64 = 0 309 while k < rl { outbuf[o] = rv[k]; o = o + 1; k = k + 1 } 310 outbuf[o] = CDA_NL as u8 311 o = o + 1 312 returned = returned + 1 313 } 314 } 315 i = i + 1 316 } 317 return o 318} 319func cda_post_mode(room_in: *u8, name_in: *u8, text_in: *u8, version:i64) -> i64 { 320 let rclean: *u8 = sts_mm(64) 321 if cda_ident(room_in, rclean, CDA_ROOM_MAX) < 1 { cda_w("{\"ok\":0,\"err\":\"bad-room\"}\n" as *u8) return CDA_EXIT_REFUSED } 322 let nclean: *u8 = sts_mm(64) 323 if cda_ident(name_in, nclean, CDA_NAME_MAX) < 1 { cda_w("{\"ok\":0,\"err\":\"bad-name\"}\n" as *u8) return CDA_EXIT_REFUSED } 324 let tclean: *u8 = sts_mm(CDA_TEXT_MAX + 8) 325 var tlen:i64=0 326 if version==2 {tlen=cda_text(text_in,tclean,CDA_TEXT_MAX)} else {tlen=cda_text_legacy(text_in,tclean,CDA_TEXT_MAX)} 327 if tlen == CDA_TEXT_TOO_LONG { cda_w("{\"ok\":0,\"err\":\"text-too-long\"}\n" as *u8);return CDA_EXIT_REFUSED } 328 if tlen == CDA_TEXT_BAD_UTF8 { cda_w("{\"ok\":0,\"err\":\"invalid-utf8\"}\n" as *u8);return CDA_EXIT_REFUSED } 329 if tlen < 1 { cda_w("{\"ok\":0,\"err\":\"empty-text\"}\n" as *u8) return CDA_EXIT_REFUSED } 330 let room: *u8 = sts_mm(64) 331 cda_demoroom(rclean, room) 332 let seq: i64 = csa_append(room, nclean, tclean, tlen) 333 if seq < 0 { 334 if seq == CSA_ERR_MSGS { cda_w("{\"ok\":0,\"err\":\"room-full-messages\"}\n" as *u8) return CDA_EXIT_REFUSED } 335 if seq == CSA_ERR_BYTES { cda_w("{\"ok\":0,\"err\":\"room-full-bytes\"}\n" as *u8) return CDA_EXIT_REFUSED } 336 if seq == CSA_ERR_BODY { cda_w("{\"ok\":0,\"err\":\"body-too-big\"}\n" as *u8) return CDA_EXIT_REFUSED } 337 if seq == CSA_ERR_LOCK { cda_w("{\"ok\":0,\"err\":\"busy-retry\"}\n" as *u8) return CDA_EXIT_REFUSED } 338 cda_w("{\"ok\":0,\"err\":\"append-failed\"}\n" as *u8) 339 return CDA_EXIT_REFUSED 340 } 341 let jb: *u8 = sts_mm(256) 342 var o: i64 = cda_cat(jb, 0, "{\"ok\":1,\"room\":\"" as *u8) 343 o = cda_cat(jb, o, room) 344 o = cda_cat(jb, o, "\",\"seq\":" as *u8) 345 o = cda_catn(jb, o, seq) 346 o = cda_cat(jb, o, "}\n" as *u8) 347 cda_wn(jb, o) 348 return CDA_EXIT_OK 349} 350func cda_post(room_in:*u8,name_in:*u8,text_in:*u8)->i64 {return cda_post_mode(room_in,name_in,text_in,1)} 351func cda_post_v2(room_in:*u8,name_in:*u8,text_in:*u8)->i64 {return cda_post_mode(room_in,name_in,text_in,2)} 352func cda_atoi(b: *u8, n: i64) -> i64 { var v: i64 = 0; var i: i64 = 0; while i < n { let c: i64 = b[i] as i64; if c < 48 { break } if c > 57 { break } v = v * 10 + (c - 48); i = i + 1 } return v } 353func cda_list(room_in: *u8, since_in: *u8) -> i64 { 354 let rclean: *u8 = sts_mm(64) 355 if cda_ident(room_in, rclean, CDA_ROOM_MAX) < 1 { cda_w("{\"ok\":0,\"err\":\"bad-room\"}\n" as *u8) return CDA_EXIT_REFUSED } 356 let room: *u8 = sts_mm(64) 357 cda_demoroom(rclean, room) 358 var since: i64 = 0 359 if since_in as i64 != 0 { since = cda_atoi(since_in, cda_len(since_in)) } 360 let raw: *u8 = sts_mm(CDA_OUTCAP) 361 let rn: i64 = csa_fetch(room, since, CDA_FETCH_MAX, raw, CDA_OUTCAP) 362 let jb: *u8 = sts_mm(CDA_OUTCAP) 363 var o: i64 = cda_cat(jb, 0, "{\"ok\":1,\"room\":\"" as *u8) 364 o = cda_cat(jb, o, room) 365 o = cda_cat(jb, o, "\",\"msgs\":[" as *u8) 366 var total: i64 = 0 367 if rn > 0 { 368 var i: i64 = 0 369 var first: i64 = 1 370 while i < rn { 371 var ls: i64 = i 372 while i < rn { if raw[i] == (CDA_NL as u8) { break } i = i + 1 } 373 let le: i64 = i 374 i = i + 1 375 if le - ls > 4 { if raw[ls] == (109 as u8) { if raw[ls+1] == (CDA_PIPE as u8) { 376 var p: i64 = ls + 2 377 var seq: i64 = 0 378 while p < le { let c: i64 = raw[p] as i64; if c == CDA_PIPE { break } seq = seq * 10 + (c - 48); p = p + 1 } 379 p = p + 1 380 var ep: i64 = 0 381 while p < le { let c2: i64 = raw[p] as i64; if c2 == CDA_PIPE { break } ep = ep * 10 + (c2 - 48); p = p + 1 } 382 p = p + 1 383 var kind: i64 = 0 384 while p < le { let c3: i64 = raw[p] as i64; if c3 == CDA_PIPE { break } kind = kind * 10 + (c3 - 48); p = p + 1 } 385 p = p + 1 386 let snds: i64 = p 387 while p < le { if raw[p] == (CDA_PIPE as u8) { break } p = p + 1 } 388 let snde: i64 = p 389 p = p + 1 390 let hxs: i64 = p 391 if kind == CDA_KIND_TEXT { 392 let hxlen: i64 = le - hxs 393 let bodyb: *u8 = sts_mm(hxlen / 2 + 4) 394 var bo: i64 = 0 395 var hh: i64 = hxs 396 var okhex: i64 = 1 397 while hh + 1 < le { 398 let hi: i64 = cda_hexv(raw[hh] as i64) 399 let lo: i64 = cda_hexv(raw[hh+1] as i64) 400 if hi < 0 { okhex = 0; break } 401 if lo < 0 { okhex = 0; break } 402 bodyb[bo] = (hi * 16 + lo) as u8 403 bo = bo + 1 404 hh = hh + 2 405 } 406 if okhex == 1 { 407 if first == 0 { jb[o] = 44 as u8; o = o + 1 } 408 first = 0 409 o = cda_cat(jb, o, "{\"seq\":" as *u8) 410 o = cda_catn(jb, o, seq) 411 o = cda_cat(jb, o, ",\"t\":" as *u8) 412 o = cda_catn(jb, o, ep) 413 o = cda_cat(jb, o, ",\"from\":\"" as *u8) 414 o = cda_jesc(jb, o, (raw as i64 + snds) as *u8, snde - snds) 415 o = cda_cat(jb, o, "\",\"text\":\"" as *u8) 416 o = cda_jesc(jb, o, bodyb, bo) 417 o = cda_cat(jb, o, "\"}" as *u8) 418 total = total + 1 419 } 420 } 421 } } } 422 } 423 } 424 o = cda_cat(jb, o, "],\"total\":" as *u8) 425 o = cda_catn(jb, o, total) 426 o = cda_cat(jb, o, "}\n" as *u8) 427 cda_wn(jb, o) 428 return CDA_EXIT_OK 429} 430func cda_usage() -> i64 { 431 cda_w("{\"ok\":0,\"err\":\"usage: post <room> <name> <text> | list <room> [since]\"}\n" as *u8) 432 return CDA_EXIT_USAGE 433} 434func main(argc: i64, argv: *i64) -> i64 { 435 if argc < 2 { let u: i64 = cda_usage() sys_exit(u) return u } 436 let v: *u8 = argv[1] as *u8 437 if cda_eq(v, "post-v2" as *u8) == 1 { 438 if argc < 5 { return cda_usage() } 439 return cda_post_v2(argv[2] as *u8,argv[3] as *u8,argv[4] as *u8) 440 } 441 if cda_eq(v, "post" as *u8) == 1 { 442 if argc < 5 { let u: i64 = cda_usage() sys_exit(u) return u } 443 let r: i64 = cda_post(argv[2] as *u8, argv[3] as *u8, argv[4] as *u8) 444 sys_exit(r) 445 return r 446 } 447 if cda_eq(v, "list" as *u8) == 1 { 448 if argc < 3 { let u: i64 = cda_usage() sys_exit(u) return u } 449 var since: *u8 = 0 as *u8 450 if argc > 3 { since = argv[3] as *u8 } 451 let r: i64 = cda_list(argv[2] as *u8, since) 452 sys_exit(r) 453 return r 454 } 455 let u2: i64 = cda_usage() 456 sys_exit(u2) 457 return u2 458}