nx_ini_writer.nx source
↩ module page · 110 lines · 3499 B
1// ini_writer.nx -- serialise INI-style config.
2//
3// Companion to ini.nx (reader). Stream builder emits files
4// that ini.nx round-trips exactly:
5// [section]
6// key = value
7// ; comment
8//
9// We always use \" = \" (space-equals-space) separator and LF line
10// endings. This matches typical Unix convention; Windows tools
11// tolerate LF.
12//
13// Invariants:
14// IW1 Keys and values emitted verbatim -- no escaping. INI
15// has no standard escape form; values with newlines are
16// inherently unsafe in INI, caller pre-sanitises.
17// IW2 ini_writer_section groups the next keys under that name
18// until another section is opened.
19// IW3 Round-trip: ini.nx parsing our output yields the
20// original (section, key, value) tuples.
21
22// nx_safety_envelope:
23// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
24// sil_target: SIL1
25// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
26// verdict: NOT_YET_EVALUATED
27
28import "nx_syscalls.nx"
29
30const IW_ERR_SHORT: i64 = -1
31
32func iw_put(out: *u8, cap: i64, off: i64, src: *u8, n: i64) -> i64 {
33 if off + n > cap { return IW_ERR_SHORT }
34 var i: i64 = 0
35 while i < n {
36 out[off + i] = src[i]
37 i = i + 1
38 }
39 return off + n
40}
41
42// Write \"[name]\\n\".
43func ini_writer_section(out: *u8, cap: i64, off: i64,
44 name: *u8, name_len: i64) -> i64 {
45 var cur: i64 = off
46 cur = iw_put(out, cap, cur, "[", 1)
47 if cur < 0 { return cur }
48 cur = iw_put(out, cap, cur, name, name_len)
49 if cur < 0 { return cur }
50 cur = iw_put(out, cap, cur, "]\n", 2)
51 return cur
52}
53
54// Write \"key = value\\n\".
55func ini_writer_kv(out: *u8, cap: i64, off: i64,
56 key: *u8, key_len: i64,
57 val: *u8, val_len: i64) -> i64 {
58 var cur: i64 = off
59 cur = iw_put(out, cap, cur, key, key_len)
60 if cur < 0 { return cur }
61 cur = iw_put(out, cap, cur, " = ", 3)
62 if cur < 0 { return cur }
63 cur = iw_put(out, cap, cur, val, val_len)
64 if cur < 0 { return cur }
65 cur = iw_put(out, cap, cur, "\n", 1)
66 return cur
67}
68
69// Write \"; comment\\n\".
70func ini_writer_comment(out: *u8, cap: i64, off: i64,
71 text: *u8, text_len: i64) -> i64 {
72 var cur: i64 = off
73 cur = iw_put(out, cap, cur, "; ", 2)
74 if cur < 0 { return cur }
75 cur = iw_put(out, cap, cur, text, text_len)
76 if cur < 0 { return cur }
77 cur = iw_put(out, cap, cur, "\n", 1)
78 return cur
79}
80
81// Blank line separator.
82func ini_writer_blank(out: *u8, cap: i64, off: i64) -> i64 {
83 return iw_put(out, cap, off, "\n", 1)
84}
85
86// Compile-only smoke.
87func main() -> i64 {
88 let out: *u8 = sys_mmap(256)
89 var off: i64 = 0
90
91 off = ini_writer_comment(out, 256, off, "Nishi config", 12)
92 if off < 0 { return 1 }
93 off = ini_writer_section(out, 256, off, "db", 2)
94 if off < 0 { return 2 }
95 off = ini_writer_kv(out, 256, off, "host", 4, "127.0.0.1", 9)
96 if off < 0 { return 3 }
97 off = ini_writer_kv(out, 256, off, "port", 4, "5432", 4)
98 if off < 0 { return 4 }
99 off = ini_writer_blank(out, 256, off)
100 if off < 0 { return 5 }
101 off = ini_writer_section(out, 256, off, "log", 3)
102 if off < 0 { return 6 }
103 off = ini_writer_kv(out, 256, off, "level", 5, "info", 4)
104 if off < 0 { return 7 }
105
106 // First two bytes: \"; \"
107 if out[0] != 0x3B { return 8 } // ';'
108 if out[1] != 0x20 { return 9 } // ' '
109 return 0
110}