code wiki / _hdl_build / nx_vizsla_bills.nx

nx_vizsla_bills.nx source

↩ module page · 623 lines · 21737 B

1// nx_vizsla_bills.nx -- NISHI VIZSLA: the recurring BILLS / payables tracker. 2// Operator directive 2026-06-14: "manage all our bills". V1 budget tracks 3// money HISTORY; this tracks the recurring OBLIGATIONS -- what is due, when, how 4// much, and whether THIS month is paid -- so a bill is never silently missed. 5// Inherits the CID-ledger / civil-date substrate from V4 (nx_vizsla_relate). 6// 7// THEORY AS DATA (bills.txt): payees, amounts, due-days, autopay are all file 8// content (law 11/25) -- adding/retuning a bill is an edit, never a recompile. 9// 10// Commands (argv[1]): 11// load <bill_log> <prefix> [segid] PAID rows -> canonical records -> CID 12// keys "bill:nxc1-..." on seg_store 13// (additive, idempotent by construction). 14// status <prefix> <bills> <today> per bill: next due date + is THIS month 15// paid -> PAID / UPCOMING / DUE / OVERDUE, 16// + owed_cents for everything unpaid. 17// today via argv = deterministic. 18// 19// File formats (line records, # comments): 20// bills: CONF soon_days <n> 21// BILL <id> <payee> <amount_cents> <due_day> MONTHLY <AUTO|MANUAL> 22// (due_day 1..28; only MONTHLY this rung -- other cadences fail loud) 23// bill_log: PAID <date> <bill-id> <amount_cents> <note> (date YYYY-MM-DD) 24// 25// "Paid this month" = a PAID record for that bill dated in today's calendar 26// month. due_this_month = (today.year, today.month, due_day). state: 27// PAID (paid this month) -> next due = next month 28// else days_until = due_this_month - today: 29// days_until < 0 -> OVERDUE 30// 0 <= days_until <= soon -> DUE 31// days_until > soon -> UPCOMING 32// owed_cents = sum of every UNPAID bill's amount (due+overdue+upcoming) = what 33// you still owe this month. autopay shown (AUTO = no action) but money still 34// counts as owed until recorded paid. 35// 36// Determinism: stdout has no clocks (today via argv). Loud-fail: missing files, 37// cadence != MONTHLY, autopay not AUTO/MANUAL, malformed dates/days = exit 1. 38// EXCEED (honest, feature-class): consumer bill apps silo reminders from the 39// money ledger; here bills + budget + the relationship/notes brief share ONE 40// additive tamper-evident substrate and surface together in the daily digest. 41// 42// module: nishi-core.vizsla.bills 43// depends: nishi-core.lib.syscalls, nishi-core.lib.canon_cid, nishi-core.lib.seg_store 44// capability: VIZSLA_BILLS 45// spec: knowledge/specs/2026-06-10-nishi-vizsla-ladder.md license_tier: ORIGINAL 46import "nx_syscalls.nx" 47import "nx_canon_cid.nx" 48import "nx_seg_store.nx" 49const K_MAGIC_1970: i64 = 1970 50const K_MAGIC_146097: i64 = 146097 51const K_MAGIC_719468: i64 = 719468 52const K_MAGIC_4096: i64 = 4096 53const K_MAGIC_2048: i64 = 2048 54const K_MAGIC_65536: i64 = 65536 55 56func bl_slen(s: *u8) -> i64 { 57 var n: i64 = 0 58 while s[n] != (0 as u8) { n = n + 1 } 59 return n 60} 61 62func bl_p(s: *u8) -> i64 { 63 sys_write(1, s, bl_slen(s)) 64 return 0 65} 66 67func bl_eq(a: *u8, b: *u8) -> i64 { 68 var i: i64 = 0 69 var go: i64 = 1 70 while go == 1 { 71 if a[i] != b[i] { return 0 } 72 if a[i] == (0 as u8) { return 1 } 73 i = i + 1 74 } 75 return 0 76} 77 78func bl_dup(s: *u8) -> *u8 { 79 let n: i64 = bl_slen(s) 80 let d: *u8 = sys_mmap(n + 2) 81 var i: i64 = 0 82 while i <= n { d[i] = s[i]; i = i + 1 } 83 return d 84} 85 86func bl_find(list: *i64, n: i64, s: *u8) -> i64 { 87 var i: i64 = 0 88 while i < n { 89 if bl_eq(list[i] as *u8, s) == 1 { return i } 90 i = i + 1 91 } 92 return 0 - 1 93} 94 95func bl_atoi(s: *u8) -> i64 { 96 var v: i64 = 0 97 var i: i64 = 0 98 while s[i] != (0 as u8) { 99 let d: i64 = (s[i] as i64) - 48 100 if d >= 0 { if d <= 9 { v = v * 10 + d } } 101 i = i + 1 102 } 103 return v 104} 105 106func bl_isws(c: i64) -> i64 { 107 if c == 32 { return 1 } 108 if c == 13 { return 1 } 109 if c == 9 { return 1 } 110 return 0 111} 112 113func bl_tok(b: *u8, off: i64, lend: i64, dst: *u8, cap: i64) -> i64 { 114 var p: i64 = off 115 var go: i64 = 1 116 while go == 1 { 117 if p >= lend { go = 0 } else { 118 if bl_isws(b[p] as i64) == 1 { p = p + 1 } else { go = 0 } 119 } 120 } 121 var t: i64 = 0 122 go = 1 123 while go == 1 { 124 if p >= lend { go = 0 } else { 125 if bl_isws(b[p] as i64) == 1 { go = 0 } else { 126 if t < cap - 1 { dst[t] = b[p]; t = t + 1 } 127 p = p + 1 128 } 129 } 130 } 131 dst[t] = 0 as u8 132 return p 133} 134 135func bl_cat(dst: *u8, off: i64, s: *u8) -> i64 { 136 var i: i64 = 0 137 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 } 138 return off + i 139} 140 141func bl_catn(dst: *u8, off: i64, v: i64) -> i64 { 142 var o: i64 = off 143 var m: i64 = v 144 if m < 0 { dst[o] = 45 as u8; o = o + 1; m = 0 - m } 145 let t: *u8 = sys_mmap(28) 146 var k: i64 = 0 147 if m == 0 { t[0] = 48 as u8; k = 1 } 148 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 149 var i: i64 = 0 150 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 } 151 return o + k 152} 153 154func bl_cat2(dst: *u8, off: i64, v: i64) -> i64 { 155 var o: i64 = off 156 if v < 10 { dst[o] = 48 as u8; o = o + 1 } 157 return bl_catn(dst, o, v) 158} 159 160func bl_kmemeq(b: *u8, off: i64, n: i64, s: *u8) -> i64 { 161 if bl_slen(s) != n { return 0 } 162 var i: i64 = 0 163 while i < n { 164 if b[off + i] != s[i] { return 0 } 165 i = i + 1 166 } 167 return 1 168} 169 170func bl_field(rec: *u8, rl: i64, want: *u8, out: *u8, cap: i64) -> i64 { 171 if rl < 8 { return 0 } 172 let nf: i64 = ss_r32(rec, 4) 173 var off: i64 = 8 174 var fi: i64 = 0 175 while fi < nf { 176 if off + 8 > rl { return 0 } 177 let kl: i64 = ss_r32(rec, off) 178 let koff: i64 = off + 4 179 let vl: i64 = ss_r32(rec, koff + kl) 180 let voff: i64 = koff + kl + 4 181 if bl_kmemeq(rec, koff, kl, want) == 1 { 182 var t: i64 = 0 183 while t < vl { 184 if t < cap - 1 { out[t] = rec[voff + t] } 185 t = t + 1 186 } 187 if t > cap - 1 { t = cap - 1 } 188 out[t] = 0 as u8 189 return 1 190 } 191 off = voff + vl 192 fi = fi + 1 193 } 194 return 0 195} 196 197func bl_isbill(b: *u8, koff: i64, kl: i64) -> i64 { 198 if kl <= 5 { return 0 } 199 return bl_kmemeq(b, koff, 5, "bill:" as *u8) 200} 201 202// civil date "YYYY-MM-DD" -> days since 1970-01-01 (Hinnant); -1 if malformed 203func bl_datedays(s: *u8) -> i64 { 204 let nums: *i64 = sys_mmap(8 * 4) as *i64 205 var nn: i64 = 0 206 var cur: i64 = 0 207 var indig: i64 = 0 208 var i: i64 = 0 209 var go: i64 = 1 210 while go == 1 { 211 let c: i64 = s[i] as i64 212 var isd: i64 = 0 213 if c >= 48 { if c <= 57 { isd = 1 } } 214 if isd == 1 { cur = cur * 10 + (c - 48); indig = 1 } 215 if isd == 0 { 216 if indig == 1 { 217 if nn < 4 { nums[nn] = cur; nn = nn + 1 } 218 cur = 0 219 indig = 0 220 } 221 } 222 if c == 0 { go = 0 } 223 i = i + 1 224 } 225 if nn != 3 { return 0 - 1 } 226 var y: i64 = nums[0] 227 let m: i64 = nums[1] 228 let d: i64 = nums[2] 229 if y < K_MAGIC_1970 { return 0 - 1 } 230 if m < 1 { return 0 - 1 } 231 if m > 12 { return 0 - 1 } 232 if d < 1 { return 0 - 1 } 233 if d > 31 { return 0 - 1 } 234 if m <= 2 { y = y - 1 } 235 let era: i64 = y / 400 236 let yoe: i64 = y - era * 400 237 var mp: i64 = m - 3 238 if m <= 2 { mp = m + 9 } 239 let doy: i64 = (153 * mp + 2) / 5 + d - 1 240 let doe: i64 = yoe * 365 + yoe / 4 - yoe / 100 + doy 241 return era * K_MAGIC_146097 + doe - K_MAGIC_719468 242} 243 244// year*100+month from a "YYYY-MM-..." string (the calendar-month key) 245func bl_ym(s: *u8) -> i64 { 246 let nums: *i64 = sys_mmap(8 * 4) as *i64 247 var nn: i64 = 0 248 var cur: i64 = 0 249 var indig: i64 = 0 250 var i: i64 = 0 251 var go: i64 = 1 252 while go == 1 { 253 let c: i64 = s[i] as i64 254 var isd: i64 = 0 255 if c >= 48 { if c <= 57 { isd = 1 } } 256 if isd == 1 { cur = cur * 10 + (c - 48); indig = 1 } 257 if isd == 0 { 258 if indig == 1 { 259 if nn < 4 { nums[nn] = cur; nn = nn + 1 } 260 cur = 0 261 indig = 0 262 } 263 } 264 if c == 0 { go = 0 } 265 i = i + 1 266 } 267 if nn < 2 { return 0 - 1 } 268 return nums[0] * 100 + nums[1] 269} 270 271func bl_builddate(y: i64, m: i64, d: i64, out: *u8) -> i64 { 272 var o: i64 = 0 273 o = bl_catn(out, o, y) 274 out[o] = 45 as u8; o = o + 1 275 o = bl_cat2(out, o, m) 276 out[o] = 45 as u8; o = o + 1 277 o = bl_cat2(out, o, d) 278 out[o] = 0 as u8 279 return o 280} 281 282func bl_log(lpath: *u8, tag: *u8, rep: *u8, o: i64) -> i64 { 283 let fd: i64 = sys_openat_append(lpath, 0x1a4) 284 if fd <= 0 { return 0 } 285 let hdr: *u8 = sys_mmap(256) 286 var ho: i64 = 0 287 ho = bl_cat(hdr, ho, "VIZSLA-BILL epoch=" as *u8) 288 ho = bl_catn(hdr, ho, sys_now_realtime_sec()) 289 ho = bl_cat(hdr, ho, " cmd=" as *u8) 290 ho = bl_cat(hdr, ho, tag) 291 ho = bl_cat(hdr, ho, "\n" as *u8) 292 sys_write(fd, hdr, ho) 293 sys_write(fd, rep, o) 294 sys_close(fd) 295 return 0 296} 297 298func bl_prefixlog(prefix: *u8, out: *u8) -> i64 { 299 var o: i64 = 0 300 o = bl_cat(out, o, prefix) 301 o = bl_cat(out, o, "bills.log" as *u8) 302 out[o] = 0 as u8 303 return o 304} 305 306// bills table: bf[0]=id bf[1]=payee bf[2]=amount bf[3]=due_day bf[4]=autopay 307// bf[5]=nbills bf[6]=soon_days 308func bl_load_bills(cpath: *u8) -> *i64 { 309 let szp: *i64 = sys_mmap(16) as *i64 310 let cb: *u8 = ss_readall(cpath, szp) 311 let csz: i64 = szp[0] 312 if csz <= 0 { return 0 as *i64 } 313 let bf: *i64 = sys_mmap(8 * 16) as *i64 314 bf[0] = sys_mmap(8 * 128); bf[1] = sys_mmap(8 * 128); bf[2] = sys_mmap(8 * 128) 315 bf[3] = sys_mmap(8 * 128); bf[4] = sys_mmap(8 * 128); bf[5] = 0; bf[6] = 7 316 let b_id: *i64 = bf[0] as *i64 317 let b_payee: *i64 = bf[1] as *i64 318 let b_amt: *i64 = bf[2] as *i64 319 let b_day: *i64 = bf[3] as *i64 320 let b_auto: *i64 = bf[4] as *i64 321 let t0: *u8 = sys_mmap(128) 322 let t1: *u8 = sys_mmap(128) 323 let t2: *u8 = sys_mmap(128) 324 let t3: *u8 = sys_mmap(128) 325 let t4: *u8 = sys_mmap(128) 326 let t5: *u8 = sys_mmap(128) 327 var i: i64 = 0 328 while i < csz { 329 var e: i64 = i 330 var go: i64 = 1 331 while go == 1 { 332 if e >= csz { go = 0 } else { 333 if cb[e] == (10 as u8) { go = 0 } else { e = e + 1 } 334 } 335 } 336 var p: i64 = bl_tok(cb, i, e, t0, 128) 337 if t0[0] == (35 as u8) { t0[0] = 0 as u8 } 338 if bl_eq(t0, "CONF" as *u8) == 1 { 339 p = bl_tok(cb, p, e, t1, 128) 340 p = bl_tok(cb, p, e, t2, 128) 341 if bl_eq(t1, "soon_days" as *u8) == 1 { bf[6] = bl_atoi(t2) } 342 } 343 if bl_eq(t0, "BILL" as *u8) == 1 { 344 // BILL <id> <payee> <amount_cents> <due_day> MONTHLY <AUTO|MANUAL> 345 p = bl_tok(cb, p, e, t1, 128); p = bl_tok(cb, p, e, t2, 128); p = bl_tok(cb, p, e, t3, 128) 346 p = bl_tok(cb, p, e, t4, 128); p = bl_tok(cb, p, e, t5, 128) 347 let auto: *u8 = sys_mmap(64) 348 bl_tok(cb, p, e, auto, 64) 349 if bl_eq(t5, "MONTHLY" as *u8) == 0 { return 0 as *i64 } 350 if bl_eq(auto, "AUTO" as *u8) == 0 { if bl_eq(auto, "MANUAL" as *u8) == 0 { return 0 as *i64 } } 351 let dd: i64 = bl_atoi(t4) 352 if dd < 1 { return 0 as *i64 } 353 if dd > 28 { return 0 as *i64 } 354 if bf[5] < 128 { 355 b_id[bf[5]] = bl_dup(t1) as i64 356 b_payee[bf[5]] = bl_dup(t2) as i64 357 b_amt[bf[5]] = bl_atoi(t3) 358 b_day[bf[5]] = dd 359 b_auto[bf[5]] = bl_dup(auto) as i64 360 bf[5] = bf[5] + 1 361 } 362 } 363 i = e + 1 364 } 365 if bf[5] < 1 { return 0 as *i64 } 366 return bf 367} 368 369// ---------------- LOAD (PAID journal) ---------------- 370func bl_cmd_load(argc: i64, argv: *i64) -> i64 { 371 if argc < 4 { bl_p("VIZSLA-BILL load needs <bill_log> <prefix> -- fail loud\n" as *u8); return 1 } 372 let lpath: *u8 = argv[2] as *u8 373 let prefix: *u8 = argv[3] as *u8 374 var segid: i64 = 0 375 if argc > 4 { segid = bl_atoi(argv[4] as *u8) } 376 if segid == 0 { segid = sys_now_us() } 377 378 let szp: *i64 = sys_mmap(16) as *i64 379 let b: *u8 = ss_readall(lpath, szp) 380 let sz: i64 = szp[0] 381 if sz <= 0 { bl_p("VIZSLA-BILL bill_log MISSING/EMPTY -- fail loud\n" as *u8); return 1 } 382 383 let keys: *i64 = sys_mmap(8 * 8) as *i64 384 let vals: *i64 = sys_mmap(8 * 8) as *i64 385 keys[0] = "k" as *u8 as i64; keys[1] = "date" as *u8 as i64; keys[2] = "a" as *u8 as i64 386 keys[3] = "b" as *u8 as i64; keys[4] = "c" as *u8 as i64 387 vals[0] = "paid" as *u8 as i64 388 389 let t0: *u8 = sys_mmap(128) 390 let f1: *u8 = sys_mmap(128) 391 let f2: *u8 = sys_mmap(128) 392 let f3: *u8 = sys_mmap(128) 393 let f4: *u8 = sys_mmap(256) 394 let pp: *i64 = sys_mmap(16) as *i64 395 let ll: *i64 = sys_mmap(16) as *i64 396 397 let w: *i64 = ss_begin() 398 let seen: *i64 = sys_mmap(8 * K_MAGIC_4096) as *i64 399 var nseen: i64 = 0 400 var scanned: i64 = 0 401 var added: i64 = 0 402 var dup_infile: i64 = 0 403 var dup_instore: i64 = 0 404 405 var i: i64 = 0 406 while i < sz { 407 var e: i64 = i 408 var go: i64 = 1 409 while go == 1 { 410 if e >= sz { go = 0 } else { 411 if b[e] == (10 as u8) { go = 0 } else { e = e + 1 } 412 } 413 } 414 var p: i64 = bl_tok(b, i, e, t0, 128) 415 if t0[0] == (35 as u8) { t0[0] = 0 as u8 } 416 if bl_eq(t0, "PAID" as *u8) == 1 { 417 scanned = scanned + 1 418 p = bl_tok(b, p, e, f1, 128); p = bl_tok(b, p, e, f2, 128); p = bl_tok(b, p, e, f3, 128); p = bl_tok(b, p, e, f4, 256) 419 if bl_datedays(f1) < 0 { bl_p("VIZSLA-BILL PAID bad date -- fail loud\n" as *u8); return 1 } 420 if f2[0] == (0 as u8) { bl_p("VIZSLA-BILL PAID missing bill-id -- fail loud\n" as *u8); return 1 } 421 vals[1] = f1 as i64; vals[2] = f2 as i64; vals[3] = f3 as i64; vals[4] = f4 as i64 422 let enc: *u8 = sys_mmap(K_MAGIC_2048) 423 let el: i64 = canon_encode(keys, vals, 5, enc) 424 let cid: *u8 = sys_mmap(96) 425 cid_of(enc, el, cid) 426 let kbuf: *u8 = sys_mmap(128) 427 var ko: i64 = 0 428 ko = bl_cat(kbuf, ko, "bill:" as *u8) 429 ko = bl_cat(kbuf, ko, cid) 430 kbuf[ko] = 0 as u8 431 var fresh: i64 = 1 432 if bl_find(seen, nseen, kbuf) >= 0 { fresh = 0; dup_infile = dup_infile + 1 } 433 if fresh == 1 { 434 if nseen < K_MAGIC_4096 { seen[nseen] = bl_dup(kbuf) as i64; nseen = nseen + 1 } 435 } 436 if fresh == 1 { 437 if ss_get_idx(prefix, kbuf, pp, ll) == 1 { fresh = 0; dup_instore = dup_instore + 1 } 438 } 439 if fresh == 1 { 440 if ss_add(w, 1, kbuf, enc, el) != 0 { bl_p("VIZSLA-BILL writer full -- fail loud\n" as *u8); return 1 } 441 added = added + 1 442 } 443 } 444 i = e + 1 445 } 446 447 var committed: i64 = 0 448 if added > 0 { 449 if ss_commit(prefix, w, segid) != 0 { bl_p("VIZSLA-BILL commit FAILED -- fail loud\n" as *u8); return 1 } 450 committed = 1 451 } 452 453 let rep: *u8 = sys_mmap(K_MAGIC_4096) 454 var o: i64 = 0 455 o = bl_cat(rep, o, "VIZSLA-BILL-LOAD scanned=" as *u8) 456 o = bl_catn(rep, o, scanned) 457 o = bl_cat(rep, o, " new=" as *u8) 458 o = bl_catn(rep, o, added) 459 o = bl_cat(rep, o, " dup_infile=" as *u8) 460 o = bl_catn(rep, o, dup_infile) 461 o = bl_cat(rep, o, " dup_instore=" as *u8) 462 o = bl_catn(rep, o, dup_instore) 463 o = bl_cat(rep, o, " segment=" as *u8) 464 if committed == 1 { 465 o = bl_cat(rep, o, "seg-" as *u8) 466 o = bl_catn(rep, o, segid) 467 } else { 468 o = bl_cat(rep, o, "none" as *u8) 469 } 470 o = bl_cat(rep, o, "\n" as *u8) 471 sys_write(1, rep, o) 472 let lp: *u8 = sys_mmap(512) 473 bl_prefixlog(prefix, lp) 474 bl_log(lp, "load" as *u8, rep, o) 475 return 0 476} 477 478// find a (bill-id, ym) match in the collected PAID arrays 479func bl_paid_match(pid: *i64, pym: *i64, npaid: i64, billid: *u8, ym: i64) -> i64 { 480 var i: i64 = 0 481 while i < npaid { 482 if pym[i] == ym { if bl_eq(pid[i] as *u8, billid) == 1 { return 1 } } 483 i = i + 1 484 } 485 return 0 486} 487 488// ---------------- STATUS ---------------- 489func bl_cmd_status(argc: i64, argv: *i64) -> i64 { 490 if argc < 5 { bl_p("VIZSLA-BILL status needs <prefix> <bills> <today> -- fail loud\n" as *u8); return 1 } 491 let prefix: *u8 = argv[2] as *u8 492 let bf: *i64 = bl_load_bills(argv[3] as *u8) 493 if (bf as i64) == 0 { bl_p("VIZSLA-BILL bills MISSING/MALFORMED -- fail loud\n" as *u8); return 1 } 494 let tstr: *u8 = argv[4] as *u8 495 let today: i64 = bl_datedays(tstr) 496 if today < 0 { bl_p("VIZSLA-BILL bad today date -- fail loud\n" as *u8); return 1 } 497 let today_ym: i64 = bl_ym(tstr) 498 let ty: i64 = today_ym / 100 499 let tm: i64 = today_ym - ty * 100 500 let soon: i64 = bf[6] 501 502 let b_id: *i64 = bf[0] as *i64 503 let b_payee: *i64 = bf[1] as *i64 504 let b_amt: *i64 = bf[2] as *i64 505 let b_day: *i64 = bf[3] as *i64 506 let b_auto: *i64 = bf[4] as *i64 507 508 // collect PAID records (bill-id, year*100+month) from the store 509 let pid: *i64 = sys_mmap(8 * K_MAGIC_2048) as *i64 510 let pym: *i64 = sys_mmap(8 * K_MAGIC_2048) as *i64 511 var npaid: i64 = 0 512 let segs: *i64 = sys_mmap(8 * 260) as *i64 513 let ns: i64 = ss_manifest(prefix, segs) 514 let fa: *u8 = sys_mmap(128) 515 let fdt: *u8 = sys_mmap(128) 516 let fk: *u8 = sys_mmap(64) 517 var s: i64 = 0 518 while s < ns { 519 let path: *u8 = sys_mmap(512) 520 var po: i64 = 0 521 po = bl_cat(path, po, prefix); po = bl_cat(path, po, segs[s] as *u8); po = bl_cat(path, po, ".docs" as *u8) 522 path[po] = 0 as u8 523 let zp: *i64 = sys_mmap(16) as *i64 524 let bb: *u8 = ss_readall(path, zp) 525 let bz: i64 = zp[0] 526 var j: i64 = 0 527 while j + 9 <= bz { 528 let kind: i64 = bb[j] 529 let kl: i64 = ss_r32(bb, j + 1) 530 let koff: i64 = j + 5 531 let vl: i64 = ss_r32(bb, koff + kl) 532 let voff: i64 = koff + kl + 4 533 var take: i64 = 0 534 if kind == 1 { take = bl_isbill(bb, koff, kl) } 535 if take == 1 { 536 let rec: *u8 = (bb as i64 + voff) as *u8 537 if bl_field(rec, vl, "k" as *u8, fk, 64) == 1 { 538 if bl_eq(fk, "paid" as *u8) == 1 { 539 bl_field(rec, vl, "a" as *u8, fa, 128) 540 bl_field(rec, vl, "date" as *u8, fdt, 128) 541 if npaid < K_MAGIC_2048 { pid[npaid] = bl_dup(fa) as i64; pym[npaid] = bl_ym(fdt); npaid = npaid + 1 } 542 } 543 } 544 } 545 j = voff + vl 546 } 547 s = s + 1 548 } 549 550 let rep: *u8 = sys_mmap(K_MAGIC_65536) 551 var o: i64 = 0 552 var npd: i64 = 0 553 var nup: i64 = 0 554 var ndue: i64 = 0 555 var nov: i64 = 0 556 var owed: i64 = 0 557 var nexty: i64 = ty 558 var nextm: i64 = tm 559 var i: i64 = 0 560 while i < bf[5] { 561 let day: i64 = b_day[i] 562 let paid: i64 = bl_paid_match(pid, pym, npaid, b_id[i] as *u8, today_ym) 563 let dthis: *u8 = sys_mmap(32) 564 bl_builddate(ty, tm, day, dthis) 565 let due_days: i64 = bl_datedays(dthis) 566 nexty = ty; nextm = tm + 1 567 if nextm > 12 { nextm = 1; nexty = ty + 1 } 568 let dnext: *u8 = sys_mmap(32) 569 bl_builddate(nexty, nextm, day, dnext) 570 var state: i64 = 0 571 var duedate: *u8 = dthis 572 if paid == 1 { state = 4; duedate = dnext } 573 if paid == 0 { 574 let du: i64 = due_days - today 575 if du < 0 { state = 3 } 576 if du >= 0 { if du <= soon { state = 2 } else { state = 1 } } 577 owed = owed + b_amt[i] 578 } 579 o = bl_cat(rep, o, "VIZSLA-BILL id=" as *u8) 580 o = bl_cat(rep, o, b_id[i] as *u8) 581 o = bl_cat(rep, o, " payee=" as *u8) 582 o = bl_cat(rep, o, b_payee[i] as *u8) 583 o = bl_cat(rep, o, " amount=" as *u8) 584 o = bl_catn(rep, o, b_amt[i]) 585 o = bl_cat(rep, o, " due=" as *u8) 586 o = bl_cat(rep, o, duedate) 587 o = bl_cat(rep, o, " autopay=" as *u8) 588 o = bl_cat(rep, o, b_auto[i] as *u8) 589 o = bl_cat(rep, o, " state=" as *u8) 590 if state == 4 { o = bl_cat(rep, o, "PAID\n" as *u8); npd = npd + 1 } 591 if state == 1 { o = bl_cat(rep, o, "UPCOMING\n" as *u8); nup = nup + 1 } 592 if state == 2 { o = bl_cat(rep, o, "DUE\n" as *u8); ndue = ndue + 1 } 593 if state == 3 { o = bl_cat(rep, o, "OVERDUE\n" as *u8); nov = nov + 1 } 594 i = i + 1 595 } 596 o = bl_cat(rep, o, "VIZSLA-BILL-VERDICT bills=" as *u8) 597 o = bl_catn(rep, o, bf[5]) 598 o = bl_cat(rep, o, " paid=" as *u8) 599 o = bl_catn(rep, o, npd) 600 o = bl_cat(rep, o, " upcoming=" as *u8) 601 o = bl_catn(rep, o, nup) 602 o = bl_cat(rep, o, " due=" as *u8) 603 o = bl_catn(rep, o, ndue) 604 o = bl_cat(rep, o, " overdue=" as *u8) 605 o = bl_catn(rep, o, nov) 606 o = bl_cat(rep, o, " owed_cents=" as *u8) 607 o = bl_catn(rep, o, owed) 608 o = bl_cat(rep, o, "\n" as *u8) 609 sys_write(1, rep, o) 610 let lp: *u8 = sys_mmap(512) 611 bl_prefixlog(prefix, lp) 612 bl_log(lp, "status" as *u8, rep, o) 613 return 0 614} 615 616func main(argc: i64, argv: *i64) -> i64 { 617 if argc < 2 { bl_p("VIZSLA-BILL usage: load|status -- fail loud\n" as *u8); return 1 } 618 let cmd: *u8 = argv[1] as *u8 619 if bl_eq(cmd, "load" as *u8) == 1 { return bl_cmd_load(argc, argv) } 620 if bl_eq(cmd, "status" as *u8) == 1 { return bl_cmd_status(argc, argv) } 621 bl_p("VIZSLA-BILL unknown command (load|status) -- fail loud\n" as *u8) 622 return 1 623}