|  | @@ -0,0 +1,166 @@
 | 
	
		
			
				|  |  | +//! fnmatch implementation
 | 
	
		
			
				|  |  | +#![no_std]
 | 
	
		
			
				|  |  | +#![feature(alloc)]
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +extern crate alloc;
 | 
	
		
			
				|  |  | +extern crate platform;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +use alloc::vec::Vec;
 | 
	
		
			
				|  |  | +use platform::types::*;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +pub const FNM_NOMATCH: c_int = 1;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +pub const FNM_NOESCAPE: c_int = 1;
 | 
	
		
			
				|  |  | +pub const FNM_PATHNAME: c_int = 2;
 | 
	
		
			
				|  |  | +pub const FNM_PERIOD:   c_int = 4;
 | 
	
		
			
				|  |  | +pub const FNM_CASEFOLD: c_int = 8;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#[derive(Debug)]
 | 
	
		
			
				|  |  | +enum Token {
 | 
	
		
			
				|  |  | +    Any,
 | 
	
		
			
				|  |  | +    Char(u8),
 | 
	
		
			
				|  |  | +    Match(bool, Vec<u8>),
 | 
	
		
			
				|  |  | +    Wildcard,
 | 
	
		
			
				|  |  | +    // TODO: FNM_EXTMATCH, which is basically a whole another custom regex
 | 
	
		
			
				|  |  | +    // format that's ambigious and ugh. The C standard library is really bloaty
 | 
	
		
			
				|  |  | +    // and I sure hope we can get away with delaying this as long as possible.
 | 
	
		
			
				|  |  | +    // If you need to implement this, you can contact jD91mZM2 for assistance
 | 
	
		
			
				|  |  | +    // in reading this code in case it's ugly.
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +unsafe fn next_token(pattern: &mut *const c_char, flags: c_int) -> Option<Token> {
 | 
	
		
			
				|  |  | +    let c = **pattern as u8;
 | 
	
		
			
				|  |  | +    if c == 0 {
 | 
	
		
			
				|  |  | +        return None;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    *pattern = pattern.offset(1);
 | 
	
		
			
				|  |  | +    Some(match c {
 | 
	
		
			
				|  |  | +        b'\\' if flags & FNM_NOESCAPE == FNM_NOESCAPE => {
 | 
	
		
			
				|  |  | +            let c = **pattern as u8;
 | 
	
		
			
				|  |  | +            if c == 0 {
 | 
	
		
			
				|  |  | +                // Trailing backslash. Maybe error here?
 | 
	
		
			
				|  |  | +                return None;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            *pattern = pattern.offset(1);
 | 
	
		
			
				|  |  | +            Token::Char(c)
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  | +        b'?' => Token::Any,
 | 
	
		
			
				|  |  | +        b'*' => Token::Wildcard,
 | 
	
		
			
				|  |  | +        b'[' => {
 | 
	
		
			
				|  |  | +            let mut matches = Vec::new();
 | 
	
		
			
				|  |  | +            let invert = if **pattern as u8 == b'!' {
 | 
	
		
			
				|  |  | +                *pattern = pattern.offset(1);
 | 
	
		
			
				|  |  | +                true
 | 
	
		
			
				|  |  | +            } else { false };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            loop {
 | 
	
		
			
				|  |  | +                let mut c = **pattern as u8;
 | 
	
		
			
				|  |  | +                if c == 0 {
 | 
	
		
			
				|  |  | +                    break;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                *pattern = pattern.offset(1);
 | 
	
		
			
				|  |  | +                match c {
 | 
	
		
			
				|  |  | +                    b']' => break,
 | 
	
		
			
				|  |  | +                    b'\\' => {
 | 
	
		
			
				|  |  | +                        c = **pattern as u8;
 | 
	
		
			
				|  |  | +                        *pattern = pattern.offset(1);
 | 
	
		
			
				|  |  | +                        if c == 0 {
 | 
	
		
			
				|  |  | +                            // Trailing backslash. Maybe error?
 | 
	
		
			
				|  |  | +                            break;
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  | +                    },
 | 
	
		
			
				|  |  | +                    _ => ()
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                if matches.len() >= 2 && matches[matches.len() - 1] == b'-' {
 | 
	
		
			
				|  |  | +                    let len = matches.len();
 | 
	
		
			
				|  |  | +                    let start = matches[len - 2];
 | 
	
		
			
				|  |  | +                    matches.drain(len - 2..);
 | 
	
		
			
				|  |  | +                    // Exclusive range because we'll push C later
 | 
	
		
			
				|  |  | +                    for c in start..c {
 | 
	
		
			
				|  |  | +                        matches.push(c);
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                matches.push(c);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            // Otherwise, there was no closing ]. Maybe error?
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            Token::Match(invert, matches)
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  | +        c => Token::Char(c)
 | 
	
		
			
				|  |  | +    })
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#[no_mangle]
 | 
	
		
			
				|  |  | +pub unsafe extern "C" fn fnmatch(mut pattern: *const c_char, mut input: *const c_char, flags: c_int) -> c_int {
 | 
	
		
			
				|  |  | +    let pathname = flags & FNM_PATHNAME == FNM_PATHNAME;
 | 
	
		
			
				|  |  | +    let casefold = flags & FNM_CASEFOLD == FNM_CASEFOLD;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let mut leading = true;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    loop {
 | 
	
		
			
				|  |  | +        if *input == 0 {
 | 
	
		
			
				|  |  | +            return if *pattern == 0 { 0 } else { FNM_NOMATCH };
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if leading && flags & FNM_PERIOD == FNM_PERIOD {
 | 
	
		
			
				|  |  | +            if *input as u8 == b'.' && *pattern as u8 != b'.' {
 | 
	
		
			
				|  |  | +                return FNM_NOMATCH;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        leading = false;
 | 
	
		
			
				|  |  | +        match next_token(&mut pattern, flags) {
 | 
	
		
			
				|  |  | +            Some(Token::Any) => {
 | 
	
		
			
				|  |  | +                if pathname && *input as u8 == b'/' {
 | 
	
		
			
				|  |  | +                    return FNM_NOMATCH;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                input = input.offset(1);
 | 
	
		
			
				|  |  | +            },
 | 
	
		
			
				|  |  | +            Some(Token::Char(c)) => {
 | 
	
		
			
				|  |  | +                let mut a = *input as u8;
 | 
	
		
			
				|  |  | +                if casefold && a >= b'a' && a <= b'z' {
 | 
	
		
			
				|  |  | +                    a -= b'a' - b'A';
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                let mut b = c;
 | 
	
		
			
				|  |  | +                if casefold && b >= b'a' && b <= b'z' {
 | 
	
		
			
				|  |  | +                    b -= b'a' - b'A';
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                if a != b {
 | 
	
		
			
				|  |  | +                    return FNM_NOMATCH;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                if pathname && a == b'/' {
 | 
	
		
			
				|  |  | +                    leading = true;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                input = input.offset(1);
 | 
	
		
			
				|  |  | +            },
 | 
	
		
			
				|  |  | +            Some(Token::Match(invert, matches)) => {
 | 
	
		
			
				|  |  | +                if (pathname && *input as u8 == b'/') || matches.contains(&(*input as u8)) == invert {
 | 
	
		
			
				|  |  | +                    // Found it, but it's inverted! Or vise versa.
 | 
	
		
			
				|  |  | +                    return FNM_NOMATCH;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                input = input.offset(1);
 | 
	
		
			
				|  |  | +            },
 | 
	
		
			
				|  |  | +            Some(Token::Wildcard) => {
 | 
	
		
			
				|  |  | +                loop {
 | 
	
		
			
				|  |  | +                    let c = *input as u8;
 | 
	
		
			
				|  |  | +                    if c == 0 {
 | 
	
		
			
				|  |  | +                        return if *pattern == 0 { 0 } else { FNM_NOMATCH };
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    let ret = fnmatch(pattern, input, flags);
 | 
	
		
			
				|  |  | +                    if ret == FNM_NOMATCH {
 | 
	
		
			
				|  |  | +                        input = input.offset(1);
 | 
	
		
			
				|  |  | +                    } else {
 | 
	
		
			
				|  |  | +                        // Either an error or a match. Forward the return.
 | 
	
		
			
				|  |  | +                        return ret;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    if pathname && c == b'/' {
 | 
	
		
			
				|  |  | +                        // End of segment, no match yet
 | 
	
		
			
				|  |  | +                        return FNM_NOMATCH;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                unreachable!("nothing should be able to break out of the loop");
 | 
	
		
			
				|  |  | +            },
 | 
	
		
			
				|  |  | +            None => return FNM_NOMATCH // Pattern ended but there's still some input
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 |