relibc/header/fnmatch/
mod.rs1use alloc::{borrow::Cow, vec::Vec};
6use core::slice;
7
8use crate::platform::types::{c_char, c_int};
9use posix_regex::{
10 PosixRegex,
11 compile::{Collation, Range, Token},
12 tree::{Tree, TreeBuilder},
13};
14
15const ONCE: Range = Range(1, Some(1));
17
18pub const FNM_NOMATCH: c_int = 1;
20
21pub const FNM_NOESCAPE: c_int = 1;
23pub const FNM_PATHNAME: c_int = 2;
25pub const FNM_PERIOD: c_int = 4;
27pub const FNM_CASEFOLD: c_int = 8;
29pub const FNM_IGNORECASE: c_int = FNM_CASEFOLD;
31unsafe fn tokenize(mut pattern: *const u8, flags: c_int) -> Tree {
34 fn any(leading: bool, flags: c_int) -> Token {
35 let mut list = Vec::new();
36 if flags & FNM_PATHNAME == FNM_PATHNAME {
37 list.push(Collation::Char(b'/'))
38 }
39 if leading && flags & FNM_PERIOD == FNM_PERIOD {
40 list.push(Collation::Char(b'.'))
41 }
42 Token::OneOf { invert: true, list }
43 }
44 fn can_push(leading: bool, flags: c_int, c: u8) -> bool {
45 (c != b'/' || flags & FNM_PATHNAME != FNM_PATHNAME)
46 && (c != b'.' || !leading || flags & FNM_PERIOD != FNM_PERIOD)
47 }
48 fn is_leading(flags: c_int, c: u8) -> bool {
49 c == b'/' && flags & FNM_PATHNAME == FNM_PATHNAME
50 }
51
52 let mut leading = true;
53 let mut need_collapsing = false;
54
55 let mut builder = TreeBuilder::default();
56 builder.start_internal(Token::Root, Range(1, Some(1)));
57 builder.start_internal(Token::Alternative, Range(1, Some(1)));
58
59 while unsafe { *pattern != 0 } {
60 let was_leading = leading;
61 leading = false;
62
63 let c = unsafe { *pattern };
64 pattern = unsafe { pattern.offset(1) };
65
66 match (c == b'*', need_collapsing) {
67 (true, true) => continue,
68 (true, false) => need_collapsing = true,
69 (false, _) => need_collapsing = false,
70 }
71
72 let (token, range) = match c {
73 b'\\' if flags & FNM_NOESCAPE == FNM_NOESCAPE => {
74 let c = unsafe { *pattern };
75 if c == 0 {
76 break;
78 }
79 pattern = unsafe { pattern.offset(1) };
80 leading = is_leading(flags, c);
81 (Token::Char(c), ONCE)
82 }
83 b'?' => (any(was_leading, flags), ONCE),
84 b'*' => (any(was_leading, flags), Range(0, None)),
85 b'[' => {
86 let mut list: Vec<Collation> = Vec::new();
87 let invert = if unsafe { *pattern == b'!' } {
88 pattern = unsafe { pattern.offset(1) };
89 true
90 } else {
91 false
92 };
93
94 loop {
95 let mut c = unsafe { *pattern };
96 if c == 0 {
97 break;
98 }
99 pattern = unsafe { pattern.offset(1) };
100 match c {
101 b']' => break,
102 b'\\' => {
103 c = unsafe { *pattern };
104 pattern = unsafe { pattern.offset(1) };
105 if c == 0 {
106 break;
108 }
109 }
110 _ => (),
111 }
112 if unsafe { *pattern == b'-' && *pattern.offset(1) != 0 } {
113 let end = unsafe { *pattern.offset(1) };
114 pattern = unsafe { pattern.offset(2) };
115 for c in c..=end {
116 if can_push(was_leading, flags, c) {
117 list.push(Collation::Char(c));
118 }
119 }
120 } else if can_push(was_leading, flags, c) {
121 list.push(Collation::Char(c));
122 }
123 }
124 (Token::OneOf { invert, list }, ONCE)
127 }
128 c => {
129 leading = is_leading(flags, c);
130 (Token::Char(c), ONCE)
131 }
132 };
133 builder.leaf(token, range);
134 }
135 builder.leaf(Token::End, ONCE);
136 builder.finish_internal();
137 builder.finish_internal();
138 builder.finish()
139}
140
141#[unsafe(no_mangle)]
149#[linkage = "weak"] pub unsafe extern "C" fn fnmatch(
151 pattern: *const c_char,
152 input: *const c_char,
153 flags: c_int,
154) -> c_int {
155 let mut len = 0;
156 while unsafe { *input.offset(len) != 0 } {
157 len += 1;
158 }
159 let input = unsafe { slice::from_raw_parts(input.cast::<u8>(), len as usize) };
160
161 let tokens = unsafe { tokenize(pattern.cast::<u8>(), flags) };
162
163 if PosixRegex::new(Cow::Owned(tokens))
164 .case_insensitive(flags & FNM_CASEFOLD == FNM_CASEFOLD)
165 .matches_exact(input)
166 .is_some()
167 {
168 0
169 } else {
170 FNM_NOMATCH
171 }
172}