Skip to main content

relibc/header/regex/
mod.rs

1//! `regex.h` implementation.
2//!
3//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/regex.h.html>.
4
5use crate::{
6    header::string::strlen,
7    platform::types::{c_char, c_int, c_void, size_t},
8};
9use alloc::{borrow::Cow, boxed::Box};
10use core::{ptr, slice};
11use posix_regex::{PosixRegex, PosixRegexBuilder, compile::Error as CompileError, tree::Tree};
12
13/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/regex.h.html>.
14pub type regoff_t = size_t;
15
16/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/regex.h.html>.
17#[repr(C)]
18pub struct regex_t {
19    // Points to a posix_regex::Tree
20    ptr: *mut c_void,
21    cflags: c_int,
22    re_nsub: size_t,
23}
24
25/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/regex.h.html>.
26#[repr(C)]
27pub struct regmatch_t {
28    rm_so: regoff_t,
29    rm_eo: regoff_t,
30}
31
32pub const REG_EXTENDED: c_int = 1;
33pub const REG_ICASE: c_int = 2;
34pub const REG_NOSUB: c_int = 4;
35pub const REG_NEWLINE: c_int = 8;
36pub const REG_NOTBOL: c_int = 16;
37pub const REG_NOTEOL: c_int = 32;
38pub const REG_MINIMAL: c_int = 64;
39
40pub const REG_NOMATCH: c_int = 1;
41pub const REG_BADPAT: c_int = 2;
42pub const REG_ECOLLATE: c_int = 3;
43pub const REG_ECTYPE: c_int = 4;
44pub const REG_EESCAPE: c_int = 5;
45pub const REG_ESUBREG: c_int = 6;
46pub const REG_EBRACK: c_int = 7;
47pub const REG_ENOSYS: c_int = 8;
48pub const REG_EPAREN: c_int = 9;
49pub const REG_EBRACE: c_int = 10;
50pub const REG_BADBR: c_int = 11;
51pub const REG_ERANGE: c_int = 12;
52pub const REG_ESPACE: c_int = 13;
53pub const REG_BADRPT: c_int = 14;
54
55/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/regcomp.html>.
56#[unsafe(no_mangle)]
57#[linkage = "weak"] // redefined in GIT
58pub unsafe extern "C" fn regcomp(out: *mut regex_t, pat: *const c_char, cflags: c_int) -> c_int {
59    let pat = unsafe { slice::from_raw_parts(pat.cast::<u8>(), strlen(pat)) };
60    let res = PosixRegexBuilder::new(pat)
61        .with_default_classes()
62        .extended(cflags & REG_EXTENDED == REG_EXTENDED)
63        .compile_tokens();
64
65    match res {
66        Ok(branches) => {
67            let re_nsub = PosixRegex::new(Cow::Borrowed(&branches)).count_groups();
68            unsafe {
69                *out = regex_t {
70                    ptr: Box::into_raw(Box::new(branches)).cast::<c_void>(),
71
72                    cflags,
73                    re_nsub,
74                }
75            };
76            0
77        }
78        Err(CompileError::EmptyRepetition)
79        | Err(CompileError::IntegerOverflow)
80        | Err(CompileError::IllegalRange) => REG_BADBR,
81        Err(CompileError::UnclosedRepetition) => REG_EBRACE,
82        Err(CompileError::LeadingRepetition) => REG_BADRPT,
83        Err(CompileError::UnknownCollation) => REG_ECOLLATE,
84        Err(CompileError::UnknownClass(_)) => REG_ECTYPE,
85        Err(_) => REG_BADPAT,
86    }
87}
88
89/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/regfree.html>.
90#[unsafe(no_mangle)]
91#[linkage = "weak"] // redefined in GIT
92pub unsafe extern "C" fn regfree(regex: *mut regex_t) {
93    unsafe { drop(Box::from_raw((*regex).ptr)) };
94}
95
96/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/regexec.html>.
97#[unsafe(no_mangle)]
98#[linkage = "weak"] // redefined in GIT
99pub unsafe extern "C" fn regexec(
100    regex: *const regex_t,
101    input: *const c_char,
102    nmatch: size_t,
103    pmatch: *mut regmatch_t,
104    eflags: c_int,
105) -> c_int {
106    let regex = unsafe { &*regex };
107
108    // Allow specifying a compiler argument to the executor and viceversa
109    // because why not?
110    let flags = regex.cflags | eflags;
111
112    let input = unsafe { slice::from_raw_parts(input.cast::<u8>(), strlen(input)) };
113    let branches = unsafe { &*(regex.ptr.cast::<Tree>()) };
114
115    let matches = PosixRegex::new(Cow::Borrowed(branches))
116        .case_insensitive(flags & REG_ICASE == REG_ICASE)
117        .newline(flags & REG_NEWLINE == REG_NEWLINE)
118        .no_start(flags & REG_NOTBOL == REG_NOTBOL)
119        .no_end(flags & REG_NOTEOL == REG_NOTEOL)
120        .matches(input, Some(1));
121
122    if !matches.is_empty() && eflags & REG_NOSUB != REG_NOSUB && !pmatch.is_null() && nmatch > 0 {
123        let first = &matches[0];
124
125        for i in 0..nmatch {
126            let (start, end) = first.get(i).and_then(|&range| range).unwrap_or((!0, !0));
127            unsafe {
128                *pmatch.add(i) = regmatch_t {
129                    rm_so: start,
130                    rm_eo: end,
131                }
132            };
133        }
134    }
135
136    if matches.is_empty() { REG_NOMATCH } else { 0 }
137}
138
139/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/regerror.html>.
140#[unsafe(no_mangle)]
141#[linkage = "weak"] // redefined in GIT
142pub extern "C" fn regerror(
143    code: c_int,
144    _regex: *const regex_t,
145    out: *mut c_char,
146    max: size_t,
147) -> size_t {
148    let string = match code {
149        0 => "No error\0",
150        REG_NOMATCH => "No match\0",
151        REG_BADPAT => "Invalid regexp\0",
152        REG_ECOLLATE => "Unknown collating element\0",
153        REG_ECTYPE => "Unknown character class name\0",
154        REG_EESCAPE => "Trailing backslash\0",
155        REG_ESUBREG => "Invalid back reference\0",
156        REG_EBRACK => "Missing ']'\0",
157        REG_ENOSYS => "Unsupported operation\0",
158        REG_EPAREN => "Missing ')'\0",
159        REG_EBRACE => "Missing '}'\0",
160        REG_BADBR => "Invalid contents of {}\0",
161        REG_ERANGE => "Invalid character range\0",
162        REG_ESPACE => "Out of memory\0",
163        REG_BADRPT => "Repetition not preceded by valid expression\0",
164        _ => "Unknown error\0",
165    };
166
167    unsafe {
168        ptr::copy_nonoverlapping(string.as_ptr(), out.cast::<u8>(), string.len().min(max));
169    }
170
171    string.len()
172}