nx_find.nx source
↩ module page · 220 lines · 6549 B
1// nx_find.nx -- S5 stone of NISHI_SHELL_REPLACEMENT_ROADMAP.md.
2//
3// Canonical: this is the substrate-wide canonical file-tree walk +
4// filter primitive per [[feedback-bits-up-shell-replace-linux-
5// brother-tools]]. Composes nx_dir + nx_string_ops + nx_grep.
6// Replaces the bash `find` / `ls | xargs grep` patterns scattered
7// across substrate audit scripts. S6 will add recursive walks +
8// gitignore-aware exclusions (fd 2017 style); this S5 first stone
9// ships the per-level filter primitives that the recursive walker
10// composes.
11//
12// API (all pure-function over caller-provided NxDirRow arrays;
13// no syscalls in the LOGIC layer; caller composes nx_dir_list to
14// populate rows then runs filters):
15//
16// nx_find_filter_suffix(rows, n_rows, suffix, n_suffix, out_flags) -> i64
17// For each row, sets out_flags[i] = 1 iff name ends with suffix.
18// Returns count of kept rows.
19//
20// nx_find_filter_prefix(rows, n_rows, prefix, n_prefix, out_flags) -> i64
21// For each row, sets out_flags[i] = 1 iff name starts with prefix.
22// Returns count of kept rows.
23//
24// nx_find_filter_regular_files(rows, n_rows, out_flags) -> i64
25// For each row, sets out_flags[i] = 1 iff dtype == NX_DT_REG.
26// Returns count of kept rows.
27//
28// nx_find_filter_skip_dotlike(rows, n_rows, out_flags) -> i64
29// For each row, sets out_flags[i] = 1 iff NOT is_dotlike.
30//
31// nx_find_intersect(a, b, n, out_flags) -> i64
32// out_flags[i] = a[i] AND b[i]. Composes multiple filters.
33//
34// nx_find_count_kept(flags, n) -> i64
35// Sum of 1s in flags array.
36//
37// nx_find_nth_kept_index(flags, n, k) -> i64
38// Returns the index of the k-th kept row (0-based), or -1 if
39// k exceeds count. Lets the caller iterate kept rows without
40// allocating a separate index list.
41//
42// Composes:
43// [[NISHI_SHELL_REPLACEMENT_ROADMAP]] S5 (this is the stone)
44// nx_dir.nx (NxDirRow + NX_DT_* constants; SHIPPED)
45// nx_string_ops.nx (nx_str_starts_with + nx_str_ends_with; SHIPPED)
46// [[feedback-bits-up-shell-replace-linux-brother-tools]] (cardinal)
47// [[feedback-no-tool-proliferation-bit-level]] (one canonical for
48// file-tree filtering; future recursive + gitignore composers
49// all compose this)
50
51// nx_safety_envelope:
52// intended_use: "canonical file-tree filter primitive;
53// per-row predicates (suffix / prefix /
54// regular / not-dotlike) + intersection;
55// S6+ recursive walker composes this"
56// sil_target: SIL2
57// evidence: [kat_suffix_filter,
58// kat_prefix_filter,
59// kat_regular_file_filter,
60// kat_dotlike_skip,
61// kat_intersect_compose,
62// kat_count_kept,
63// kat_nth_kept_index]
64// hazard_register: [bug-tape-flags-array-overrun,
65// bug-tape-empty-suffix-matches-all,
66// bug-tape-intersect-mismatched-lengths]
67// verdict: NOT_YET_EVALUATED
68
69import "nx_syscalls.nx"
70import "nx_string_ops.nx"
71import "nx_dir.nx"
72
73// ===== Per-row suffix filter ======================================
74
75func nx_find_filter_suffix(
76 rows: *NxDirRow,
77 n_rows: i64,
78 suffix: *u8,
79 n_suffix: i64,
80 out_flags: *i64
81) -> i64 {
82 var i: i64 = 0
83 var kept: i64 = 0
84 while i < n_rows {
85 let row: *NxDirRow = nx_dir_row_at(rows, i)
86 out_flags[i] = 0
87 if n_suffix > 0 {
88 if nx_str_ends_with(row.name_ptr, row.name_len, suffix, n_suffix) == 1 {
89 out_flags[i] = 1
90 kept = kept + 1
91 }
92 }
93 i = i + 1
94 }
95 return kept
96}
97
98// ===== Per-row prefix filter ======================================
99
100func nx_find_filter_prefix(
101 rows: *NxDirRow,
102 n_rows: i64,
103 prefix: *u8,
104 n_prefix: i64,
105 out_flags: *i64
106) -> i64 {
107 var i: i64 = 0
108 var kept: i64 = 0
109 while i < n_rows {
110 let row: *NxDirRow = nx_dir_row_at(rows, i)
111 out_flags[i] = 0
112 if n_prefix > 0 {
113 if nx_str_starts_with(row.name_ptr, row.name_len, prefix, n_prefix) == 1 {
114 out_flags[i] = 1
115 kept = kept + 1
116 }
117 }
118 i = i + 1
119 }
120 return kept
121}
122
123// ===== Regular-file filter ========================================
124
125func nx_find_filter_regular_files(
126 rows: *NxDirRow,
127 n_rows: i64,
128 out_flags: *i64
129) -> i64 {
130 var i: i64 = 0
131 var kept: i64 = 0
132 while i < n_rows {
133 let row: *NxDirRow = nx_dir_row_at(rows, i)
134 out_flags[i] = 0
135 if row.dtype == NX_DT_REG {
136 out_flags[i] = 1
137 kept = kept + 1
138 }
139 i = i + 1
140 }
141 return kept
142}
143
144// ===== Skip-dotlike filter ========================================
145
146func nx_find_filter_skip_dotlike(
147 rows: *NxDirRow,
148 n_rows: i64,
149 out_flags: *i64
150) -> i64 {
151 var i: i64 = 0
152 var kept: i64 = 0
153 while i < n_rows {
154 let row: *NxDirRow = nx_dir_row_at(rows, i)
155 out_flags[i] = 0
156 if row.is_dotlike != 1 {
157 out_flags[i] = 1
158 kept = kept + 1
159 }
160 i = i + 1
161 }
162 return kept
163}
164
165// ===== Intersection of flag arrays ================================
166//
167// out_flags[i] = a[i] AND b[i]. Caller composes multiple filters
168// without allocating a temporary keep-list.
169
170func nx_find_intersect(
171 a: *i64,
172 b: *i64,
173 n: i64,
174 out_flags: *i64
175) -> i64 {
176 var i: i64 = 0
177 var kept: i64 = 0
178 while i < n {
179 if a[i] == 1 {
180 if b[i] == 1 {
181 out_flags[i] = 1
182 kept = kept + 1
183 } else {
184 out_flags[i] = 0
185 }
186 } else {
187 out_flags[i] = 0
188 }
189 i = i + 1
190 }
191 return kept
192}
193
194// ===== Count + iterate kept rows ==================================
195
196func nx_find_count_kept(flags: *i64, n: i64) -> i64 {
197 var i: i64 = 0
198 var kept: i64 = 0
199 while i < n {
200 if flags[i] == 1 { kept = kept + 1 }
201 i = i + 1
202 }
203 return kept
204}
205
206// Returns the index of the k-th (0-based) kept row, or -1 if k
207// exceeds the kept count.
208func nx_find_nth_kept_index(flags: *i64, n: i64, k: i64) -> i64 {
209 if k < 0 { return -1 }
210 var i: i64 = 0
211 var seen: i64 = 0
212 while i < n {
213 if flags[i] == 1 {
214 if seen == k { return i }
215 seen = seen + 1
216 }
217 i = i + 1
218 }
219 return -1
220}