1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
use crate::pattern;
use crate::pattern::Mode;
use bstr::{BString, ByteSlice};
#[inline]
pub fn parse_line(mut line: &[u8]) -> Option<(BString, pattern::Mode)> {
let mut mode = Mode::empty();
if line.is_empty() {
return None;
};
if line.first() == Some(&b'!') {
mode |= Mode::NEGATIVE;
line = &line[1..];
} else if line.first() == Some(&b'\\') {
let second = line.get(1);
if second == Some(&b'!') || second == Some(&b'#') {
line = &line[1..];
}
}
if line.iter().all(|b| b.is_ascii_whitespace()) {
return None;
}
let mut line = truncate_non_escaped_trailing_spaces(line);
if line.last() == Some(&b'/') {
mode |= Mode::MUST_BE_DIR;
line.pop();
}
if !line.contains(&b'/') {
mode |= Mode::NO_SUB_DIR;
}
if line.first() == Some(&b'*') && line[1..].find_byteset(br"*?[\").is_none() {
mode |= Mode::ENDS_WITH;
}
Some((line, mode))
}
fn truncate_non_escaped_trailing_spaces(buf: &[u8]) -> BString {
match buf.rfind_not_byteset(br"\ ") {
Some(pos) if pos + 1 == buf.len() => buf.into(),
None => buf.into(),
Some(start_of_non_space) => {
let mut res: BString = buf[..start_of_non_space + 1].into();
let mut trailing_bytes = buf[start_of_non_space + 1..].iter();
let mut bare_spaces = 0;
while let Some(b) = trailing_bytes.next() {
match b {
b' ' => {
bare_spaces += 1;
}
b'\\' => {
res.extend(std::iter::repeat(b' ').take(bare_spaces));
bare_spaces = 0;
if trailing_bytes.next() == Some(&b' ') {
res.push(b' ');
}
}
_ => unreachable!("BUG: this must be either backslash or space"),
}
}
res
}
}
}