fn at(bytes: &[u8], index: usize) -> u8 {
bytes.get(index).copied().unwrap_or(0)
}
fn tail(bytes: &[u8], index: usize) -> &[u8] {
bytes.get(index..).unwrap_or(&[])
}
fn is_xplatform_dir_sep(c: u8) -> bool {
c == b'/' || c == b'\\'
}
pub fn is_ntfs_dotgit(name: &[u8]) -> bool {
let mut i;
let c = at(name, 0);
if c == b'.' {
if !at(name, 1).eq_ignore_ascii_case(&b'g')
|| !at(name, 2).eq_ignore_ascii_case(&b'i')
|| !at(name, 3).eq_ignore_ascii_case(&b't')
{
return false;
}
i = 4;
} else if c == b'g' || c == b'G' {
if !at(name, 1).eq_ignore_ascii_case(&b'i')
|| !at(name, 2).eq_ignore_ascii_case(&b't')
|| at(name, 3) != b'~'
|| at(name, 4) != b'1'
{
return false;
}
i = 5;
} else {
return false;
}
loop {
let c = at(name, i);
i += 1;
if c == 0 || is_xplatform_dir_sep(c) || c == b':' {
return true;
}
if c != b'.' && c != b' ' {
return false;
}
}
}
pub fn is_ntfs_dotgitmodules(name: &[u8]) -> bool {
is_ntfs_dot_generic(name, b"gitmodules", b"gi7eba")
}
pub fn is_ntfs_dot_generic(name: &[u8], dotgit_name: &[u8], shortname_prefix: &[u8]) -> bool {
let len = dotgit_name.len();
if at(name, 0) == b'.' && strncasecmp_eq(tail(name, 1), dotgit_name, len) {
return only_spaces_and_periods(name, len + 1);
}
if strncasecmp_eq(name, dotgit_name, 6)
&& at(name, 6) == b'~'
&& (b'1'..=b'4').contains(&at(name, 7))
{
return only_spaces_and_periods(name, 8);
}
let mut saw_tilde = false;
let mut i = 0usize;
while i < 8 {
let c = at(name, i);
if c == 0 {
return false;
} else if saw_tilde {
if !c.is_ascii_digit() {
return false;
}
} else if c == b'~' {
i += 1;
if !(b'1'..=b'9').contains(&at(name, i)) {
return false;
}
saw_tilde = true;
} else if i >= 6 || c & 0x80 != 0 || c.to_ascii_lowercase() != at(shortname_prefix, i) {
return false;
}
i += 1;
}
only_spaces_and_periods(name, i)
}
fn only_spaces_and_periods(name: &[u8], mut i: usize) -> bool {
loop {
let c = at(name, i);
i += 1;
if c == 0 || c == b':' {
return true;
}
if c != b' ' && c != b'.' {
return false;
}
}
}
fn strncasecmp_eq(a: &[u8], b: &[u8], n: usize) -> bool {
for index in 0..n {
let (x, y) = (at(a, index), at(b, index));
if !x.eq_ignore_ascii_case(&y) {
return false;
}
if x == 0 {
return true;
}
}
true
}
pub fn is_hfs_dotgit(path: &[u8]) -> bool {
is_hfs_dot_generic(path, b"git")
}
pub fn is_hfs_dotgitmodules(path: &[u8]) -> bool {
is_hfs_dot_generic(path, b"gitmodules")
}
pub fn is_hfs_dot_generic(path: &[u8], needle: &[u8]) -> bool {
let mut cursor = Some(0usize);
if next_hfs_char(path, &mut cursor) != u32::from(b'.') {
return false;
}
for expected in needle {
let c = next_hfs_char(path, &mut cursor);
if c > 127 {
return false;
}
if (c as u8).to_ascii_lowercase() != *expected {
return false;
}
}
let c = next_hfs_char(path, &mut cursor);
c == 0 || c == u32::from(b'/')
}
fn next_hfs_char(path: &[u8], cursor: &mut Option<usize>) -> u32 {
loop {
let Some(position) = *cursor else {
return 0;
};
let Some((ch, width)) = pick_one_utf8_char(tail(path, position)) else {
*cursor = None;
return 0;
};
*cursor = Some(position + width);
if is_hfs_ignorable(ch) {
continue;
}
return ch;
}
}
fn is_hfs_ignorable(ch: u32) -> bool {
matches!(
ch,
0x200c..=0x200f | 0x202a..=0x202e | 0x206a..=0x206f | 0xfeff
)
}
fn pick_one_utf8_char(s: &[u8]) -> Option<(u32, usize)> {
let b = |index: usize| u32::from(at(s, index));
let s0 = b(0);
if s0 < 0x80 {
return Some((s0, 1));
}
let cont = |index: usize| b(index) & 0xc0 == 0x80;
if s0 & 0xe0 == 0xc0 {
if !cont(1) || s0 & 0xfe == 0xc0 {
return None;
}
return Some((((s0 & 0x1f) << 6) | (b(1) & 0x3f), 2));
}
if s0 & 0xf0 == 0xe0 {
if !cont(1)
|| !cont(2)
|| (s0 == 0xe0 && b(1) & 0xe0 == 0x80)
|| (s0 == 0xed && b(1) & 0xe0 == 0xa0)
|| (s0 == 0xef && b(1) == 0xbf && b(2) & 0xfe == 0xbe)
{
return None;
}
return Some((
((s0 & 0x0f) << 12) | ((b(1) & 0x3f) << 6) | (b(2) & 0x3f),
3,
));
}
if s0 & 0xf8 == 0xf0 {
if !cont(1)
|| !cont(2)
|| !cont(3)
|| (s0 == 0xf0 && b(1) & 0xf0 == 0x80)
|| (s0 == 0xf4 && b(1) > 0x8f)
|| s0 > 0xf4
{
return None;
}
return Some((
((s0 & 0x07) << 18) | ((b(1) & 0x3f) << 12) | ((b(2) & 0x3f) << 6) | (b(3) & 0x3f),
4,
));
}
None
}
pub fn is_reserved_alias(component: &[u8], name: &[u8]) -> bool {
let ntfs_tail = |rest: &[u8]| {
let mut index = 0;
loop {
match at(rest, index) {
0 | b'/' | b'\\' | b':' => return true,
b'.' | b' ' => index += 1,
_ => return false,
}
}
};
if strncasecmp_eq(component, name, name.len()) && ntfs_tail(tail(component, name.len())) {
return true;
}
if let Some(stem) = name.strip_prefix(b".")
&& !stem.is_empty()
{
let short = &stem[..stem.len().min(6)];
if strncasecmp_eq(component, short, short.len())
&& at(component, short.len()) == b'~'
&& (b'1'..=b'4').contains(&at(component, short.len() + 1))
&& ntfs_tail(tail(component, short.len() + 2))
{
return true;
}
}
let mut cursor = Some(0usize);
for expected in name {
let c = next_hfs_char(component, &mut cursor);
if c > 127 || !(c as u8).eq_ignore_ascii_case(expected) {
return false;
}
}
let c = next_hfs_char(component, &mut cursor);
c == 0 || c == u32::from(b'/')
}