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