1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2021 Tobias Kortkamp <tobik@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include "config.h"
30
31 #include <sys/types.h>
32 #include <inttypes.h>
33 #include <stdbool.h>
34
35 #include "peg.h"
36 #include "peg/grammar.h"
37 #include "peg/mtree.h"
38
39 // XXX This grammar does not yet handle
40 // - line continuations
41 // - end of line comments (also no \# for filenames)
42
43 // Prototypes
44 static RULE(character);
45 static RULE(comment);
46 static RULE(eol);
47 static RULE(file);
48 static RULE(filemode);
49 static RULE(filename);
50 static RULE(hex_number);
51 static RULE(keyword);
52 static RULE(keyword_0);
53 static RULE(keyword_cksum);
54 static RULE(keyword_device);
55 static RULE(keyword_device_format);
56 static RULE(keyword_device_value);
57 static RULE(keyword_device_value_0);
58 static RULE(keyword_device_value_1);
59 static RULE(keyword_flags);
60 static RULE(keyword_gid);
61 static RULE(keyword_gname);
62 static RULE(keyword_link);
63 static RULE(keyword_md5);
64 static RULE(keyword_mode);
65 static RULE(keyword_nlink);
66 static RULE(keyword_rmd160);
67 static RULE(keyword_sha1);
68 static RULE(keyword_sha256);
69 static RULE(keyword_sha384);
70 static RULE(keyword_sha512);
71 static RULE(keyword_size);
72 static RULE(keyword_tags);
73 static RULE(keyword_tags_value);
74 static RULE(keyword_tags_value_0);
75 static RULE(keyword_tags_value_1);
76 static RULE(keyword_tags_value_character);
77 static RULE(keyword_tags_value_delimiter);
78 static RULE(keyword_time);
79 static RULE(keyword_type);
80 static RULE(keyword_type_values);
81 static RULE(keyword_uid);
82 static RULE(keyword_uname);
83 static RULE(main_0);
84 static RULE(number);
85 static RULE(numeric_value);
86 static RULE(setstate_);
87 static RULE(unsetallstate);
88 static RULE(unsetstate_);
89 static RULE(value);
90 static RULE(whitespace);
91
RULE(whitespace)92 RULE(whitespace) { return CHAR(' ', '\t'); }
93
RULE(character)94 RULE(character) {
95 // isprint without space
96 if (!RANGE(041, 0176))
97 return REJECT;
98 return ACCEPT;
99 }
100
RULE(number)101 RULE(number) { return RANGE('0', '9'); }
102
RULE(eol)103 RULE(eol) {
104 if (ANY(whitespace))
105 if (CHAR('\n'))
106 return ACCEPT;
107 return REJECT;
108 }
109
RULE(filename)110 RULE(filename) {
111 return MATCH(value); // TODO
112 }
113
RULE(filemode)114 RULE(filemode) {
115 return MATCH(value); // TODO
116 }
117
RULE(numeric_value)118 RULE(numeric_value) { return SOME(number); }
RULE(value)119 RULE(value) { return SOME(character); }
120
RULE(hex_number)121 RULE(hex_number) {
122 if (!RANGE('0', '9'))
123 if (!RANGE('a', 'f'))
124 if (!RANGE('A', 'F'))
125 return ERROR("expected hex number");
126 return ACCEPT;
127 }
128
RULE(keyword_device)129 RULE(keyword_device) {
130 if (CAPTURE(STRING("device"), 0, PEG_MTREE_KEYWORD))
131 if (CHAR('='))
132 if (CAPTURE(MATCH(keyword_device_value), 0, PEG_MTREE_KEYWORD_VALUE))
133 return ACCEPT;
134 return REJECT;
135 }
136
RULE(keyword_device_value)137 RULE(keyword_device_value) {
138 if (!MATCH(keyword_device_value_0))
139 if (!MATCH(keyword_device_value_1))
140 if (!SOME(number))
141 return REJECT;
142 return ACCEPT;
143 }
144
RULE(keyword_device_value_0)145 RULE(keyword_device_value_0) {
146 if (MATCH(keyword_device_format))
147 if (CHAR(','))
148 if (SOME(number))
149 if (CHAR(','))
150 if (SOME(number))
151 if (CHAR(','))
152 if (SOME(number))
153 return ACCEPT;
154 return REJECT;
155 }
156
RULE(keyword_device_value_1)157 RULE(keyword_device_value_1) {
158 if (MATCH(keyword_device_format))
159 if (CHAR(','))
160 if (SOME(number))
161 if (CHAR(','))
162 if (SOME(number))
163 return ACCEPT;
164 return REJECT;
165 }
166
RULE(keyword_device_format)167 RULE(keyword_device_format) {
168 return STRING("native", "386bsd", "4bsd", "bsdos", "freebsd", "hpux", "isc", "linux", "netbsd", "osf1", "sco", "solaris", "sunos", "svr3", "svr4", "ultrix");
169 }
170
RULE(keyword_flags)171 RULE(keyword_flags) {
172 if (CAPTURE(STRING("flags"), 0, PEG_MTREE_KEYWORD))
173 if (CHAR('='))
174 if (CAPTURE(MATCH(value), 0, PEG_MTREE_KEYWORD_VALUE)) // TODO
175 return ACCEPT;
176 return REJECT;
177 }
178
RULE(keyword_link)179 RULE(keyword_link) {
180 if (CAPTURE(STRING("link"), 0, PEG_MTREE_KEYWORD))
181 if (CHAR('='))
182 if (CAPTURE(MATCH(filename), 0, PEG_MTREE_KEYWORD_VALUE))
183 return ACCEPT;
184 return REJECT;
185 }
186
RULE(keyword_mode)187 RULE(keyword_mode) {
188 if (CAPTURE(STRING("mode"), 0, PEG_MTREE_KEYWORD))
189 if (CHAR('='))
190 if (CAPTURE(MATCH(filemode), 0, PEG_MTREE_KEYWORD_VALUE))
191 return ACCEPT;
192 return REJECT;
193 }
194
RULE(keyword_nlink)195 RULE(keyword_nlink) {
196 if (CAPTURE(STRING("nlink"), 0, PEG_MTREE_KEYWORD))
197 if (CHAR('='))
198 if (CAPTURE(SOME(number), 0, PEG_MTREE_KEYWORD_VALUE))
199 return ACCEPT;
200 return REJECT;
201 }
202
RULE(keyword_cksum)203 RULE(keyword_cksum) {
204 if (CAPTURE(STRING("cksum"), 0, PEG_MTREE_KEYWORD))
205 if (CHAR('='))
206 if (CAPTURE(SOME(number), 0, PEG_MTREE_KEYWORD_VALUE)) // TODO?
207 return ACCEPT;
208 return REJECT;
209 }
210
RULE(keyword_md5)211 RULE(keyword_md5) {
212 if (CAPTURE(STRING("md5", "md5digest"), 0, PEG_MTREE_KEYWORD))
213 if (CHAR('='))
214 if (CAPTURE(REPEAT(hex_number, 32), 0, PEG_MTREE_KEYWORD_VALUE))
215 return ACCEPT;
216 return REJECT;
217 }
218
RULE(keyword_rmd160)219 RULE(keyword_rmd160) {
220 if (CAPTURE(STRING("ripemd160digest", "rmd160", "rmd160digest"), 0, PEG_MTREE_KEYWORD))
221 if (CHAR('='))
222 if (CAPTURE(REPEAT(hex_number, 40), 0, PEG_MTREE_KEYWORD_VALUE))
223 return ACCEPT;
224 return REJECT;
225 }
226
RULE(keyword_sha1)227 RULE(keyword_sha1) {
228 if (CAPTURE(STRING("sha1", "sha1digest"), 0, PEG_MTREE_KEYWORD))
229 if (CHAR('='))
230 if (CAPTURE(REPEAT(hex_number, 40), 0, PEG_MTREE_KEYWORD_VALUE))
231 return ACCEPT;
232 return REJECT;
233 }
234
RULE(keyword_sha256)235 RULE(keyword_sha256) {
236 if (CAPTURE(STRING("sha256", "sha256digest"), 0, PEG_MTREE_KEYWORD))
237 if (CHAR('='))
238 if (CAPTURE(REPEAT(hex_number, 64), 0, PEG_MTREE_KEYWORD_VALUE))
239 return ACCEPT;
240 return REJECT;
241 }
242
RULE(keyword_sha384)243 RULE(keyword_sha384) {
244 if (CAPTURE(STRING("sha384", "sha384digest"), 0, PEG_MTREE_KEYWORD))
245 if (CHAR('='))
246 if (CAPTURE(REPEAT(hex_number, 96), 0, PEG_MTREE_KEYWORD_VALUE))
247 return ACCEPT;
248 return REJECT;
249 }
250
RULE(keyword_sha512)251 RULE(keyword_sha512) {
252 if (CAPTURE(STRING("sha512", "sha512digest"), 0, PEG_MTREE_KEYWORD))
253 if (CHAR('='))
254 if (CAPTURE(REPEAT(hex_number, 128), 0, PEG_MTREE_KEYWORD_VALUE))
255 return ACCEPT;
256 return REJECT;
257 }
258
RULE(keyword_size)259 RULE(keyword_size) {
260 if (CAPTURE(STRING("size"), 0, PEG_MTREE_KEYWORD))
261 if (CHAR('='))
262 if (CAPTURE(MATCH(numeric_value), 0, PEG_MTREE_KEYWORD_VALUE))
263 return ACCEPT;
264 return REJECT;
265 }
266
RULE(keyword_tags)267 RULE(keyword_tags) {
268 if (CAPTURE(STRING("tags"), 0, PEG_MTREE_KEYWORD))
269 if (CHAR('='))
270 if (CAPTURE(SOME(keyword_tags_value), 0, PEG_MTREE_KEYWORD_VALUE))
271 return ACCEPT;
272 return REJECT;
273 }
274
RULE(keyword_tags_value_character)275 RULE(keyword_tags_value_character) {
276 // isprint without space and ,
277 if (!RANGE(041, 053))
278 if (!RANGE(055, 0176))
279 return REJECT;
280 return ACCEPT;
281 }
282
RULE(keyword_tags_value_delimiter)283 RULE(keyword_tags_value_delimiter) { return CHAR(','); }
284
RULE(keyword_tags_value)285 RULE(keyword_tags_value) {
286 if (!MATCH(keyword_tags_value_0))
287 if (!MATCH(keyword_tags_value_1))
288 return REJECT;
289 return ACCEPT;
290 }
291
RULE(keyword_tags_value_0)292 RULE(keyword_tags_value_0) {
293 if (ANY(keyword_tags_value_delimiter))
294 if (SOME(keyword_tags_value_character))
295 if (MATCH(keyword_tags_value_delimiter))
296 return ACCEPT;
297 return REJECT;
298 }
299
RULE(keyword_tags_value_1)300 RULE(keyword_tags_value_1) {
301 if (ANY(keyword_tags_value_delimiter))
302 if (MATCH(keyword_tags_value_character))
303 return ACCEPT;
304 return REJECT;
305 }
306
RULE(keyword_time)307 RULE(keyword_time) {
308 if (STRING("time"))
309 if (CHAR('='))
310 if (MATCH(value)) // TODO
311 return ACCEPT;
312 return REJECT;
313 }
314
RULE(keyword_type)315 RULE(keyword_type) {
316 if (CAPTURE(STRING("type"), 0, PEG_MTREE_KEYWORD))
317 if (CHAR('='))
318 if (CAPTURE(MATCH(keyword_type_values), 0, PEG_MTREE_KEYWORD_VALUE))
319 return ACCEPT;
320 return REJECT;
321 }
322
RULE(keyword_type_values)323 RULE(keyword_type_values) {
324 if (!STRING("block", "char", "dir", "fifo", "file", "link", "socket"))
325 return ERROR("invald value for 'type' keyword");
326 return ACCEPT;
327 }
328
RULE(keyword_gid)329 RULE(keyword_gid) {
330 if (CAPTURE(STRING("gid"), 0, PEG_MTREE_KEYWORD))
331 if (CHAR('='))
332 if (CAPTURE(MATCH(numeric_value), 0, PEG_MTREE_KEYWORD_VALUE))
333 return ACCEPT;
334 return REJECT;
335 }
336
RULE(keyword_uid)337 RULE(keyword_uid) {
338 if (CAPTURE(STRING("uid"), 0, PEG_MTREE_KEYWORD))
339 if (CHAR('='))
340 if (CAPTURE(MATCH(numeric_value), 0, PEG_MTREE_KEYWORD_VALUE))
341 return ACCEPT;
342 return REJECT;
343 }
344
RULE(keyword_gname)345 RULE(keyword_gname) {
346 if (CAPTURE(STRING("gname"), 0, PEG_MTREE_KEYWORD))
347 if (CHAR('='))
348 if (CAPTURE(MATCH(value), 0, PEG_MTREE_KEYWORD_VALUE))
349 return ACCEPT;
350 return REJECT;
351 }
352
RULE(keyword_uname)353 RULE(keyword_uname) {
354 if (CAPTURE(STRING("uname"), 0, PEG_MTREE_KEYWORD))
355 if (CHAR('='))
356 if (CAPTURE(MATCH(value), 0, PEG_MTREE_KEYWORD_VALUE))
357 return ACCEPT;
358 return REJECT;
359 }
360
RULE(keyword)361 RULE(keyword) {
362 if (SOME(whitespace))
363 if (MATCH(keyword_0))
364 return ACCEPT;
365 return REJECT;
366 }
367
RULE(keyword_0)368 RULE(keyword_0) {
369 if (!STRING("ignore", "ignore="))
370 if (!STRING("nochange", "nochange="))
371 if (!STRING("optional", "optional="))
372 if (!MATCH(keyword_device))
373 if (!MATCH(keyword_flags))
374 if (!MATCH(keyword_gid))
375 if (!MATCH(keyword_gname))
376 if (!MATCH(keyword_link))
377 if (!MATCH(keyword_mode))
378 if (!MATCH(keyword_nlink))
379 if (!MATCH(keyword_cksum))
380 if (!MATCH(keyword_md5))
381 if (!MATCH(keyword_rmd160))
382 if (!MATCH(keyword_sha1))
383 if (!MATCH(keyword_sha256))
384 if (!MATCH(keyword_sha384))
385 if (!MATCH(keyword_sha512))
386 if (!MATCH(keyword_size))
387 if (!MATCH(keyword_tags))
388 if (!MATCH(keyword_time))
389 if (!MATCH(keyword_type))
390 if (!MATCH(keyword_uid))
391 if (!MATCH(keyword_uname))
392 return REJECT;
393 return ACCEPT;
394 }
395
RULE(comment)396 RULE(comment) {
397 if (ANY(whitespace))
398 if (CHAR('#'))
399 if (TO("\n"))
400 if (MATCH(eol))
401 return ACCEPT;
402 return REJECT;
403 }
404
RULE(file)405 RULE(file) {
406 if (ANY(whitespace))
407 if (CAPTURE(MATCH(filename), 0, PEG_MTREE_FILENAME))
408 if (ANY(keyword))
409 if (CAPTURE(MATCH(eol), 0, PEG_MTREE_FILENAME_ACCEPT))
410 return ACCEPT;
411 return REJECT;
412 }
413
RULE(setstate_)414 RULE(setstate_) {
415 if (ANY(whitespace))
416 if (STRING("/set"))
417 if (ANY(keyword))
418 if (MATCH(eol))
419 return ACCEPT;
420 return REJECT;
421 }
422
RULE(unsetstate_)423 RULE(unsetstate_) {
424 if (ANY(whitespace))
425 if (STRING("/unset"))
426 if (SOME(keyword))
427 if (MATCH(eol))
428 return ACCEPT;
429 return REJECT;
430 }
431
RULE(unsetallstate)432 RULE(unsetallstate) {
433 if (ANY(whitespace))
434 if (STRING("/unset"))
435 if (SOME(whitespace))
436 if (STRING("all"))
437 if (MATCH(eol))
438 return ACCEPT;
439 return REJECT;
440 }
441
RULE(main_0)442 RULE(main_0) {
443 if (!MATCH(comment))
444 if (!MATCH(setstate_))
445 if (!MATCH(unsetallstate))
446 if (!MATCH(unsetstate_))
447 if (!MATCH(file))
448 if (!MATCH(eol))
449 return REJECT;
450 return ACCEPT;
451 }
452
RULE(peg_mtree_decode)453 RULE(peg_mtree_decode) {
454 if (SOME(main_0))
455 if (EOS())
456 return ACCEPT;
457 return REJECT;
458 }
459