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}