//! `regex.h` implementation. //! //! See . use crate::{ header::string::strlen, platform::types::{c_char, c_int, c_void, size_t}, }; use alloc::{borrow::Cow, boxed::Box}; use core::{ptr, slice}; use posix_regex::{PosixRegex, PosixRegexBuilder, compile::Error as CompileError, tree::Tree}; /// See . pub type regoff_t = size_t; /// See . #[repr(C)] pub struct regex_t { // Points to a posix_regex::Tree ptr: *mut c_void, cflags: c_int, re_nsub: size_t, } /// See . #[repr(C)] pub struct regmatch_t { rm_so: regoff_t, rm_eo: regoff_t, } pub const REG_EXTENDED: c_int = 1; pub const REG_ICASE: c_int = 2; pub const REG_NOSUB: c_int = 4; pub const REG_NEWLINE: c_int = 8; pub const REG_NOTBOL: c_int = 16; pub const REG_NOTEOL: c_int = 32; pub const REG_MINIMAL: c_int = 64; pub const REG_NOMATCH: c_int = 1; pub const REG_BADPAT: c_int = 2; pub const REG_ECOLLATE: c_int = 3; pub const REG_ECTYPE: c_int = 4; pub const REG_EESCAPE: c_int = 5; pub const REG_ESUBREG: c_int = 6; pub const REG_EBRACK: c_int = 7; pub const REG_ENOSYS: c_int = 8; pub const REG_EPAREN: c_int = 9; pub const REG_EBRACE: c_int = 10; pub const REG_BADBR: c_int = 11; pub const REG_ERANGE: c_int = 12; pub const REG_ESPACE: c_int = 13; pub const REG_BADRPT: c_int = 14; /// See . #[unsafe(no_mangle)] #[linkage = "weak"] // redefined in GIT pub unsafe extern "C" fn regcomp(out: *mut regex_t, pat: *const c_char, cflags: c_int) -> c_int { let pat = unsafe { slice::from_raw_parts(pat as *const u8, strlen(pat)) }; let res = PosixRegexBuilder::new(pat) .with_default_classes() .extended(cflags & REG_EXTENDED == REG_EXTENDED) .compile_tokens(); match res { Ok(branches) => { let re_nsub = PosixRegex::new(Cow::Borrowed(&branches)).count_groups(); unsafe { *out = regex_t { ptr: Box::into_raw(Box::new(branches)) as *mut c_void, cflags, re_nsub, } }; 0 } Err(CompileError::EmptyRepetition) | Err(CompileError::IntegerOverflow) | Err(CompileError::IllegalRange) => REG_BADBR, Err(CompileError::UnclosedRepetition) => REG_EBRACE, Err(CompileError::LeadingRepetition) => REG_BADRPT, Err(CompileError::UnknownCollation) => REG_ECOLLATE, Err(CompileError::UnknownClass(_)) => REG_ECTYPE, Err(_) => REG_BADPAT, } } /// See . #[unsafe(no_mangle)] #[linkage = "weak"] // redefined in GIT pub unsafe extern "C" fn regfree(regex: *mut regex_t) { unsafe { Box::from_raw((*regex).ptr) }; } /// See . #[unsafe(no_mangle)] #[linkage = "weak"] // redefined in GIT pub unsafe extern "C" fn regexec( regex: *const regex_t, input: *const c_char, nmatch: size_t, pmatch: *mut regmatch_t, eflags: c_int, ) -> c_int { let regex = unsafe { &*regex }; // Allow specifying a compiler argument to the executor and viceversa // because why not? let flags = regex.cflags | eflags; let input = unsafe { slice::from_raw_parts(input as *const u8, strlen(input)) }; let branches = unsafe { &*(regex.ptr as *mut Tree) }; let matches = PosixRegex::new(Cow::Borrowed(&branches)) .case_insensitive(flags & REG_ICASE == REG_ICASE) .newline(flags & REG_NEWLINE == REG_NEWLINE) .no_start(flags & REG_NOTBOL == REG_NOTBOL) .no_end(flags & REG_NOTEOL == REG_NOTEOL) .matches(input, Some(1)); if !matches.is_empty() && eflags & REG_NOSUB != REG_NOSUB && !pmatch.is_null() && nmatch > 0 { let first = &matches[0]; for i in 0..nmatch { let (start, end) = first.get(i).and_then(|&range| range).unwrap_or((!0, !0)); unsafe { *pmatch.add(i) = regmatch_t { rm_so: start, rm_eo: end, } }; } } if matches.is_empty() { REG_NOMATCH } else { 0 } } /// See . #[unsafe(no_mangle)] #[linkage = "weak"] // redefined in GIT pub extern "C" fn regerror( code: c_int, _regex: *const regex_t, out: *mut c_char, max: size_t, ) -> size_t { let string = match code { 0 => "No error\0", REG_NOMATCH => "No match\0", REG_BADPAT => "Invalid regexp\0", REG_ECOLLATE => "Unknown collating element\0", REG_ECTYPE => "Unknown character class name\0", REG_EESCAPE => "Trailing backslash\0", REG_ESUBREG => "Invalid back reference\0", REG_EBRACK => "Missing ']'\0", REG_ENOSYS => "Unsupported operation\0", REG_EPAREN => "Missing ')'\0", REG_EBRACE => "Missing '}'\0", REG_BADBR => "Invalid contents of {}\0", REG_ERANGE => "Invalid character range\0", REG_ESPACE => "Out of memory\0", REG_BADRPT => "Repetition not preceded by valid expression\0", _ => "Unknown error\0", }; unsafe { ptr::copy_nonoverlapping( string.as_ptr(), out as *mut u8, string.len().min(max as usize), ); } string.len() }