nx_verify_port_core.nx source
↩ module page · 150 lines · 6820 B
1// nx_verify_port_core.nx -- the portability linter's SCANNER (importable; nx_verify's `portability` verb
2// + nx_portability_gate both use THIS one implementation, DRY). A LOGIC-layer source must not touch the
3// OS directly: raw "/proc" "/sys" "/dev" "/tmp" string literals or raw __syscall( outside the seams.
4// //-comments are skipped (docs may NAME the law); strings are scanned (couplings live in strings).
5// license_tier: ORIGINAL
6import "nx_syscalls.nx"
7
8const VP_NL: i64 = 10
9const VP_ASCII_0: i64 = 48 // '0' (decimal print)
10const VP_COMMENT_LEN: i64 = 2 // "//" is two chars
11
12func vp_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
13func vp_putn(v: i64) -> i64 {
14 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 }
15 var m: i64 = v; if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
16 let d: *u8 = sys_mmap(24); var k: i64 = 0
17 while m > 0 { d[k] = (VP_ASCII_0 + (m % 10)) as u8; m = m / 10; k = k + 1 }
18 let o: *u8 = sys_mmap(24); var i: i64 = 0
19 while i < k { o[i] = d[k-1-i]; i = i + 1 } sys_write(1, o, k)
20 return 0
21}
22// NUL-terminated lit matches buf at i (bounded by n)?
23func vp_matchat(buf: *u8, i: i64, n: i64, lit: *u8) -> i64 {
24 var k: i64 = 0
25 while lit[k] != (0 as u8) { if i + k >= n { return 0 } if buf[i+k] != lit[k] { return 0 } k = k + 1 }
26 return 1
27}
28// substring: does NUL-terminated needle appear in NUL-terminated s?
29func vp_name_has(s: *u8, needle: *u8) -> i64 {
30 var i: i64 = 0
31 while s[i] != (0 as u8) {
32 var k: i64 = 0; var m: i64 = 1
33 while needle[k] != (0 as u8) { if s[i+k] == (0 as u8) { m = 0; k = k + 1 } else { if s[i+k] != needle[k] { m = 0 } k = k + 1 } }
34 if m == 1 { return 1 }
35 i = i + 1
36 }
37 return 0
38}
39const VP_RD_CAP: i64 = 1048576 // per-file scan cap
40const VP_C_SCANNED: i64 = 0 // census counts[] slots
41const VP_C_COUPLED: i64 = 1
42const VP_C_HITS: i64 = 2
43const VP_C_EXEMPT: i64 = 3
44const VP_PATH_CAP: i64 = 1024
45const VP_DENT_BUF: i64 = 65536 // getdents64 batch (proven sizing)
46
47// bounded whole-file read (no fstat dependence -- procfs-safe pattern)
48func vp_read(path: *u8, buf: *u8, cap: i64) -> i64 {
49 let fd: i64 = sys_openat_rd(path)
50 if fd < 0 { return 0 }
51 var got: i64 = 0
52 var go: i64 = 1
53 while go == 1 { let r: i64 = sys_read(fd, (buf as i64 + got) as *u8, cap - got); if r > 0 { got = got + r; if got >= cap { go = 0 } } else { go = 0 } }
54 sys_close(fd)
55 return got
56}
57// structural exemption by filename (the seams + fixtures where OS-access legitimately lives)
58func vp_exempt_name(name: *u8) -> i64 {
59 if vp_name_has(name, "nx_os_" as *u8) == 1 { return 1 }
60 if vp_name_has(name, "nx_syscalls" as *u8) == 1 { return 1 }
61 if vp_name_has(name, "_gate" as *u8) == 1 { return 1 }
62 if vp_name_has(name, "_core.nx" as *u8) == 1 { return 1 }
63 return 0
64}
65// does NUL-terminated s end with NUL-terminated suf?
66func vp_ends(s: *u8, suf: *u8) -> i64 {
67 var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 }
68 var m: i64 = 0; while suf[m] != (0 as u8) { m = m + 1 }
69 if m > n { return 0 }
70 var i: i64 = 0
71 while i < m { if s[n - m + i] != suf[i] { return 0 } i = i + 1 }
72 return 1
73}
74
75// count OS-coupling hits in buf[0..n); //-comments skipped, string literals scanned
76func vp_scan(buf: *u8, n: i64) -> i64 {
77 var count: i64 = 0
78 var i: i64 = 0
79 var incomm: i64 = 0
80 while i < n {
81 if buf[i] == (VP_NL as u8) { incomm = 0; i = i + 1 } else {
82 if incomm == 1 { i = i + 1 } else {
83 if vp_matchat(buf, i, n, "//" as *u8) == 1 { incomm = 1; i = i + VP_COMMENT_LEN } else {
84 var hit: i64 = 0
85 if vp_matchat(buf, i, n, "\"/proc" as *u8) == 1 { hit = 1 }
86 if vp_matchat(buf, i, n, "\"/sys" as *u8) == 1 { hit = 1 }
87 if vp_matchat(buf, i, n, "\"/dev" as *u8) == 1 { hit = 1 }
88 if vp_matchat(buf, i, n, "\"/tmp" as *u8) == 1 { hit = 1 }
89 if vp_matchat(buf, i, n, "__syscall(" as *u8) == 1 { hit = 1 }
90 if hit == 1 { count = count + 1 }
91 i = i + 1
92 } } }
93 }
94 return count
95}
96
97// TREE CENSUS: scan every *.nx in dir (non-recursive; call per dir). Prints each COUPLED logic file
98// (the burn-down list) + a summary. counts[0]=scanned logic, [1]=coupled files, [2]=total couplings,
99// [3]=exempt(seams/gates). Returns coupled-file count. ONE process -- no per-file spawn cost.
100// (defined AFTER vp_scan: declare-before-use)
101func vp_census(dir: *u8, counts: *i64) -> i64 {
102 let fd: i64 = sys_openat_rd(dir)
103 if fd < 0 { vp_puts("vp_census: cannot open dir\n" as *u8); return 0 - 1 }
104 let dbuf: *u8 = sys_mmap(VP_DENT_BUF)
105 let path: *u8 = sys_mmap(VP_PATH_CAP)
106 let fbuf: *u8 = sys_mmap(VP_RD_CAP)
107 var scanned: i64 = 0
108 var coupled: i64 = 0
109 var hits_total: i64 = 0
110 var exempt: i64 = 0
111 var run: i64 = 1
112 while run == 1 {
113 let n: i64 = sys_getdents64(fd, dbuf, VP_DENT_BUF)
114 if n <= 0 { run = 0 } else {
115 var off: i64 = 0
116 while off < n {
117 let rec: *u8 = ((dbuf as i64 + off) as *u8)
118 let reclen: i64 = dirent_reclen(rec)
119 if reclen <= 0 { off = n } else {
120 let name: *u8 = dirent_name(rec)
121 if vp_ends(name, ".nx" as *u8) == 1 {
122 if vp_exempt_name(name) == 1 { exempt = exempt + 1 } else {
123 var o: i64 = 0
124 var i: i64 = 0
125 while dir[i] != (0 as u8) { path[o] = dir[i]; o = o + 1; i = i + 1 }
126 if o > 0 { if path[o-1] != (47 as u8) { path[o] = 47 as u8; o = o + 1 } }
127 i = 0
128 while name[i] != (0 as u8) { path[o] = name[i]; o = o + 1; i = i + 1 }
129 path[o] = 0 as u8
130 let fn: i64 = vp_read(path, fbuf, VP_RD_CAP - 1)
131 if fn > 0 {
132 scanned = scanned + 1
133 let h: i64 = vp_scan(fbuf, fn)
134 if h > 0 {
135 coupled = coupled + 1
136 hits_total = hits_total + h
137 vp_puts(" COUPLED " as *u8); vp_puts(name); vp_puts(" hits=" as *u8); vp_putn(h); vp_puts("\n" as *u8)
138 }
139 }
140 }
141 }
142 off = off + reclen
143 }
144 }
145 }
146 }
147 sys_close(fd)
148 counts[VP_C_SCANNED] = scanned; counts[VP_C_COUPLED] = coupled; counts[VP_C_HITS] = hits_total; counts[VP_C_EXEMPT] = exempt
149 return coupled
150}