code wiki / _hdl_build / nx_meet_lib.nx
nx_meet_lib.nx source
↩ module page · 150 lines · 7221 B
1// nx_meet_lib.nx -- shared helpers for the NISHI MEET PLATFORM organs (LIBRARY, no main; rule 15 DRY, so R2..R8
2// don't each re-implement them). String/io utils + .meet config lookup (meet|key|value) + |-delimited field
3// extraction + HTML-escape (escape DATA values, never the organ's own markup) + slug sanitation. 100% sovereign
4// (nx_cc->nxasm, no gcc/JS). license_tier: ORIGINAL
5import "nx_syscalls.nx"
6const K_MAGIC_1024: i64 = 1024
7
8func mlen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
9func mputs(s: *u8) -> i64 { sys_write(1, s, mlen(s)); return 0 }
10func mnum(v0: i64) -> i64 { var v: i64=v0; if v<0{sys_write(1,"-" as *u8,1);v=0-v} let b: *u8=sys_mmap(28); var k: i64=0; if v==0{b[0]=48 as u8;k=1} while v>0{b[k]=(48+(v%10)) as u8;v=v/10;k=k+1} let o: *u8=sys_mmap(28); var j: i64=0; while j<k{o[j]=b[k-1-j];j=j+1} sys_write(1,o,k); return 0 }
11
12// append nul-terminated s into dst at off; return new off (caller sizes dst generously).
13func mcat(dst: *u8, off: i64, s: *u8) -> i64 {
14 var o: i64 = off; var i: i64 = 0
15 while s[i] != (0 as u8) { dst[o] = s[i]; o = o + 1; i = i + 1 }
16 dst[o] = 0 as u8
17 return o
18}
19
20// append s into dst at off, HTML-escaping & < > " (apply to DATA values, never the organ's own markup). new off.
21func mesc(dst: *u8, off: i64, s: *u8) -> i64 {
22 var o: i64 = off; var i: i64 = 0
23 while s[i] != (0 as u8) {
24 let c: i64 = s[i] as i64
25 var done: i64 = 0
26 if c == 38 { o = mcat(dst, o, "&" as *u8); done = 1 }
27 if done == 0 { if c == 60 { o = mcat(dst, o, "<" as *u8); done = 1 } }
28 if done == 0 { if c == 62 { o = mcat(dst, o, ">" as *u8); done = 1 } }
29 if done == 0 { if c == 34 { o = mcat(dst, o, """ as *u8); done = 1 } }
30 if done == 0 { dst[o] = s[i]; o = o + 1; dst[o] = 0 as u8 }
31 i = i + 1
32 }
33 dst[o] = 0 as u8
34 return o
35}
36
37// text[s..e) byte-equal to lit[0..litlen)?
38func meet_seq(text: *u8, s: i64, e: i64, lit: *u8, litlen: i64) -> i64 {
39 if (e - s) != litlen { return 0 }
40 var i: i64 = 0
41 while i < litlen { if text[s+i] != lit[i] { return 0 } i = i + 1 }
42 return 1
43}
44
45// find a `meet|<key>|<value>` line; copy value into out (nul-term, cap-bounded). returns len, or -1 if absent.
46func meet_get(data: *u8, n: i64, key: *u8, out: *u8, cap: i64) -> i64 {
47 let klen: i64 = mlen(key)
48 var i: i64 = 0
49 while i < n {
50 var le: i64 = i
51 while le < n { if data[le] == (10 as u8) { break } le = le + 1 }
52 var p0: i64 = 0 - 1; var p1: i64 = 0 - 1
53 var k: i64 = i
54 while k < le {
55 if data[k] == (124 as u8) {
56 if p0 < 0 { p0 = k } else { if p1 < 0 { p1 = k } }
57 }
58 k = k + 1
59 }
60 if p0 >= 0 { if p1 >= 0 {
61 if meet_seq(data, i, p0, "meet" as *u8, 4) == 1 {
62 if meet_seq(data, p0 + 1, p1, key, klen) == 1 {
63 var w: i64 = 0; var c: i64 = p1 + 1
64 while c < le { if w < cap - 1 { out[w] = data[c]; w = w + 1 } c = c + 1 }
65 out[w] = 0 as u8
66 return w
67 }
68 }
69 } }
70 i = le + 1
71 }
72 return 0 - 1
73}
74
75// meet_get with a default fallback copied when the key is absent (so output is always complete).
76func meet_get_or(data: *u8, n: i64, key: *u8, out: *u8, cap: i64, deflt: *u8) -> i64 {
77 let r: i64 = meet_get(data, n, key, out, cap)
78 if r >= 0 { return r }
79 var w: i64 = 0
80 while deflt[w] != (0 as u8) { if w >= cap - 1 { break } out[w] = deflt[w]; w = w + 1 }
81 out[w] = 0 as u8
82 return w
83}
84
85// extract the idx-th '|'-delimited field within data[ls,le) into out (nul-term, cap-bounded). returns len.
86func mfield(data: *u8, ls: i64, le: i64, idx: i64, out: *u8, cap: i64) -> i64 {
87 var f: i64 = 0; var i: i64 = ls
88 while f < idx { if i < le { if data[i] == (124 as u8) { f = f + 1 } i = i + 1 } else { i = le; f = idx } }
89 var o: i64 = 0
90 while i < le { if data[i] == (124 as u8) { break } if o < cap - 1 { out[o] = data[i]; o = o + 1 } i = i + 1 }
91 out[o] = 0 as u8
92 return o
93}
94
95// copy only [A-Za-z0-9_-] from src into out (cap-bounded); fall back to "demo" if empty. (boundary defense)
96func meet_slug_safe(src: *u8, out: *u8, cap: i64) -> i64 {
97 var w: i64 = 0; var i: i64 = 0
98 while src[i] != (0 as u8) {
99 let c: i64 = src[i] as i64
100 var ok: i64 = 0
101 if c >= 97 { if c <= 122 { ok = 1 } }
102 if c >= 65 { if c <= 90 { ok = 1 } }
103 if c >= 48 { if c <= 57 { ok = 1 } }
104 if c == 95 { ok = 1 }
105 if c == 45 { ok = 1 }
106 if ok == 1 { if w < cap - 1 { out[w] = src[i]; w = w + 1 } }
107 i = i + 1
108 }
109 if w == 0 { out[0]=100 as u8; out[1]=101 as u8; out[2]=109 as u8; out[3]=111 as u8; w=4 }
110 out[w] = 0 as u8
111 return w
112}
113
114// --- R8 P1: per-site directory + clean-URL header nav (shared by every page emitter that imports this lib) ---
115func meet_w(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd, s, n); return 0 }
116func meet_mkdir(path: *u8) -> i64 { __syscall(83, path as i64, 493, 0, 0, 0, 0); return 0 } // mkdir 0755, ignore EEXIST
117
118// token-driven nav CSS (white-on-primary header). emit ONCE, right after wb_doc_open_branded.
119func meet_nav_css(fd: i64) -> i64 {
120 meet_w(fd, "<style>.nav{display:flex;gap:14px;flex-wrap:wrap;align-items:center}.nav a{color:#fff;text-decoration:none;opacity:.82;font-weight:600;min-height:44px;display:inline-flex;align-items:center}.nav a.on,.nav a:hover,.nav a:focus-visible{opacity:1;text-decoration:underline}</style>\n" as *u8)
121 return 0
122}
123func meet_nav_link(fd: i64, href: *u8, label: *u8, active: i64, idx: i64) -> i64 {
124 meet_w(fd, "<a href=\"" as *u8); meet_w(fd, href); meet_w(fd, "\"" as *u8)
125 if active == idx { meet_w(fd, " class=\"on\" aria-current=\"page\"" as *u8) }
126 meet_w(fd, ">" as *u8); meet_w(fd, label); meet_w(fd, "</a>" as *u8)
127 return 0
128}
129// site header: brand + the 5-link primary nav (clean URLs). active: 0=Home 1=Jobs 2=Apply 3=Hire 4=About.
130func meet_header_nav(fd: i64, brand: *u8, active: i64) -> i64 {
131 let eb: *u8 = sys_mmap(K_MAGIC_1024); mesc(eb, 0, brand)
132 meet_w(fd, "<header><div class=\"wrap\"><span class=\"brand\">" as *u8); meet_w(fd, eb)
133 meet_w(fd, "</span><nav class=\"nav\" aria-label=\"Primary\">" as *u8)
134 meet_nav_link(fd, "index.html" as *u8, "Home" as *u8, active, 0)
135 meet_nav_link(fd, "jobs.html" as *u8, "Jobs" as *u8, active, 1)
136 meet_nav_link(fd, "apply.html" as *u8, "Apply" as *u8, active, 2)
137 meet_nav_link(fd, "hire.html" as *u8, "Hire" as *u8, active, 3)
138 meet_nav_link(fd, "about.html" as *u8, "About" as *u8, active, 4)
139 meet_w(fd, "</nav></div></header>\n" as *u8)
140 return 0
141}
142
143// strip ALL control chars (<32) from s into dst at off -> CRLF/header-injection safe for email + record fields
144// (the bk_san pattern). new off (nul-terminated). Apply to UNTRUSTED submission values.
145func meet_san(dst: *u8, off: i64, s: *u8) -> i64 {
146 var o: i64 = off; var i: i64 = 0
147 while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c >= 32 { dst[o] = s[i]; o = o + 1 } i = i + 1 }
148 dst[o] = 0 as u8
149 return o
150}