code wiki / _hdl_build / nx_csv.nx
nx_csv.nx source
↩ module page · 97 lines · 6104 B
1// nx_csv.nx -- sovereign CSV -> HTML table viewer. A Nishi browser document renderer: turns a .csv into a clean
2// 0-JS HTML table the browser shows inline (first row = header). Handles quoted cells ("a,b" and "" escapes).
3// Same `html <in> <out> [dlurl]` interface as nx_docx/nx_md so nx_doc_view routes to it by registry. license_tier: ORIGINAL
4import "nx_syscalls.nx"
5import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
6const K_MAGIC_4194320: i64 = 4194320
7const K_MAGIC_4194304: i64 = 4194304
8const K_MAGIC_16777216: i64 = 16777216
9
10func cv_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8) { n=n+1 } return n }
11func cv_puts(s: *u8) -> i64 { sys_write(1, s, cv_slen(s)); return 0 }
12// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
13// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
14// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
15// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
16func cv_num(v: i64) -> i64 { nxi_out(v); return 0 }
17func cv_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8) { dst[off+i]=s[i]; i=i+1 } return off+i }
18func cv_esc1(dst: *u8, o: i64, c: i64) -> i64 {
19 if c==38 { return cv_cat(dst,o,"&" as *u8) }
20 if c==60 { return cv_cat(dst,o,"<" as *u8) }
21 if c==62 { return cv_cat(dst,o,">" as *u8) }
22 dst[o]=c as u8; return o+1
23}
24func cv_readfile(path: *u8, buf: *u8, cap: i64) -> i64 {
25 let fd: i64=sys_openat_rd(path); if fd<0 { return 0-1 }
26 var tot: i64=0; while tot<cap { let r: i64=sys_read(fd,(buf as i64+tot) as *u8,cap-tot); if r<=0 { break } tot=tot+r }
27 sys_close(fd); return tot
28}
29
30// emit one CSV row [i,end) as <tr> with <td>/<th> cells; returns new offset. tag = "td" or "th".
31func cv_row(out: *u8, off: i64, src: *u8, i: i64, end: i64, isheader: i64) -> i64 {
32 var o: i64=off
33 o=cv_cat(out,o,"<tr>" as *u8)
34 var p: i64=i
35 while p<=end {
36 if isheader==1 { o=cv_cat(out,o,"<th>" as *u8) } else { o=cv_cat(out,o,"<td>" as *u8) }
37 var quoted: i64=0
38 if p<end { if src[p]==(34 as u8) { quoted=1; p=p+1 } }
39 var run: i64=1
40 while run==1 {
41 if p>=end { run=0 } else {
42 let c: i64=src[p] as i64
43 if quoted==1 {
44 if c==34 {
45 if p+1<end { if src[p+1]==(34 as u8) { o=cv_esc1(out,o,34); p=p+2 } else { quoted=0; p=p+1 } } else { quoted=0; p=p+1 }
46 } else { o=cv_esc1(out,o,c); p=p+1 }
47 } else {
48 if c==44 { run=0 } else { o=cv_esc1(out,o,c); p=p+1 }
49 }
50 }
51 }
52 if isheader==1 { o=cv_cat(out,o,"</th>" as *u8) } else { o=cv_cat(out,o,"</td>" as *u8) }
53 if p<end { if src[p]==(44 as u8) { p=p+1 } else { p=end+1 } } else { p=end+1 }
54 }
55 o=cv_cat(out,o,"</tr>\n" as *u8)
56 return o
57}
58
59func cv_to_html(src: *u8, slen: i64, out: *u8, dlurl: *u8) -> i64 {
60 var o: i64=0
61 o=cv_cat(out,o,"<!DOCTYPE html>\n<html lang=en><head><meta charset=utf-8><meta name=viewport content=\"width=device-width,initial-scale=1\"><title>Document Preview - Nishi Office</title><style>\nbody{font:16px/1.6 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;margin:0;background:#eef1f5;color:#1a1d21}\nheader{background:#0f172a;color:#fff;padding:16px 24px}header h1{margin:0;font-size:17px;font-weight:600}\n.doc{max-width:960px;margin:22px auto;background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:24px;box-shadow:0 1px 4px rgba(15,23,42,.08);overflow-x:auto}\ntable{border-collapse:collapse;width:100%;font-size:14px}\nth,td{border:1px solid #cbd5e1;padding:7px 12px;text-align:left}\nth{background:#0f172a;color:#fff;position:sticky;top:0}\ntr:nth-child(even) td{background:#f8fafc}\n.bar{max-width:960px;margin:0 auto 6px;text-align:right}.dl{display:inline-block;padding:9px 18px;background:#2563eb;color:#fff;border-radius:8px;text-decoration:none;font-size:14px;font-weight:500}\nfooter{max-width:960px;margin:14px auto 46px;color:#64748b;font-size:13px;text-align:center}\n</style></head>\n<body>\n<header><h1>📄 Document Preview — Nishi Office</h1></header>\n<div class=doc>\n<table>\n" as *u8)
62 var i: i64=0; var firstrow: i64=1
63 while i<slen {
64 var e: i64=i
65 while e<slen { if src[e]==(10 as u8) { break } e=e+1 }
66 var end: i64=e
67 if end>i { if src[end-1]==(13 as u8) { end=end-1 } }
68 if end>i {
69 if firstrow==1 { o=cv_row(out,o,src,i,end,1); firstrow=0 } else { o=cv_row(out,o,src,i,end,0) }
70 }
71 i=e+1
72 }
73 o=cv_cat(out,o,"</table>\n</div>\n" as *u8)
74 if (dlurl as i64)!=0 { o=cv_cat(out,o,"<div class=bar><a class=dl href=\"" as *u8); o=cv_cat(out,o,dlurl); o=cv_cat(out,o,"\">⬇ Download .csv</a></div>\n" as *u8) }
75 o=cv_cat(out,o,"<footer>Rendered natively from CSV by <b>nx_csv</b> · viewable inline in the Nishi browser</footer>\n</body></html>\n" as *u8)
76 return o
77}
78
79func main(argc: i64, argv: *i64) -> i64 {
80 if argc<4 { cv_puts("usage: nx_csv html <in.csv> <out.html> [download_url]\n" as *u8); sys_exit(2); return 2 }
81 let cmd: *u8=argv[1] as *u8
82 if cmd[0]!=(104 as u8) { cv_puts("nx_csv: unknown command (html)\n" as *u8); sys_exit(2); return 2 }
83 let inp: *u8=argv[2] as *u8
84 let outp: *u8=argv[3] as *u8
85 var dlu: *u8=0 as *u8
86 if argc>4 { dlu=argv[4] as *u8 }
87 let src: *u8=sys_mmap(K_MAGIC_4194320)
88 let slen: i64=cv_readfile(inp, src, K_MAGIC_4194304)
89 if slen<0 { cv_puts("CSV-HTML-FAIL cannot-read\n" as *u8); sys_exit(1); return 1 }
90 let out: *u8=sys_mmap(K_MAGIC_16777216)
91 let n: i64=cv_to_html(src, slen, out, dlu)
92 let fd: i64=sys_openat_wr(outp, 0x1a4)
93 if fd<0 { cv_puts("CSV-HTML-FAIL cannot-write\n" as *u8); sys_exit(1); return 1 }
94 sys_write(fd, out, n); sys_close(fd)
95 cv_puts("CSV-HTML-OK path=" as *u8); cv_puts(outp); cv_puts(" bytes=" as *u8); cv_num(n); cv_puts("\n" as *u8)
96 sys_exit(0); return 0
97}