Skip to content

Commit b0a83b8

Browse files
committed
regex-capi: expose regex::escape
1 parent 770edd5 commit b0a83b8

File tree

4 files changed

+123
-1
lines changed

4 files changed

+123
-1
lines changed

regex-capi/ctest/test.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,33 @@ bool test_regex_set_options() {
531531
return passed;
532532
}
533533

534+
bool test_escape() {
535+
536+
bool passed = true;
537+
538+
const char *pattern = "^[a-z]+.*$";
539+
const char *expected_escaped = "\\^\\[a\\-z\\]\\+\\.\\*\\$";
540+
541+
const char *escaped = rure_escape_must(pattern);
542+
if (!escaped) {
543+
if (DEBUG) {
544+
fprintf(stderr,
545+
"[test_captures] expected escaped, but got no escaped\n");
546+
}
547+
passed = false;
548+
} else if(strcmp(escaped, expected_escaped) != 0) {
549+
if (DEBUG) {
550+
fprintf(stderr,
551+
"[test_captures] expected \"%s\", but got \"%s\"\n",
552+
expected_escaped, escaped);
553+
}
554+
passed = false;
555+
}
556+
rure_string_free((char *)escaped);
557+
return passed;
558+
559+
}
560+
534561
void run_test(bool (test)(), const char *name, bool *passed) {
535562
if (!test()) {
536563
*passed = false;
@@ -557,6 +584,7 @@ int main() {
557584
run_test(test_regex_set_options, "test_regex_set_options", &passed);
558585
run_test(test_regex_set_match_start, "test_regex_set_match_start",
559586
&passed);
587+
run_test(test_escape, "test_escape", &passed);
560588

561589
if (!passed) {
562590
exit(1);

regex-capi/include/rure.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,38 @@ void rure_error_free(rure_error *err);
567567
*/
568568
const char *rure_error_message(rure_error *err);
569569

570+
/*
571+
* rure_escape_must returns a NULL terminated string where all meta characters
572+
* have been escaped. If escaping fails for any reason, an error message is
573+
* printed to stderr and the process is aborted.
574+
*
575+
* The pattern given should be in UTF-8. For convenience, this accepts a C
576+
* string, which means the pattern cannot contain NULL byte.
577+
*/
578+
const char *rure_escape_must(const char *pattern);
579+
580+
/*
581+
* rure_escape returns a NULL terminated string where all meta characters have
582+
* been escaped. The pattern must be valid UTF-8 and the length corresponds to
583+
* the number of bytes in the pattern.
584+
*
585+
* error is set if there was a problem compiling the pattern (including if the
586+
* pattern is not valid UTF-8). If error is NULL, then no error information
587+
* is returned. In all cases, if an error occurs, NULL is returned.
588+
*
589+
* The pointer returned must not be freed directly. Instead, it should be freed
590+
* by calling rure_string_free.
591+
*/
592+
const char *rure_escape(const uint8_t *pattern, size_t length,
593+
rure_error *error);
594+
595+
/*
596+
* rure_string_free frees the string given.
597+
*
598+
* This must be called at most once per string.
599+
*/
600+
void rure_string_free(char *s);
601+
570602
#ifdef __cplusplus
571603
}
572604
#endif

regex-capi/src/error.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use ::std::ffi;
12
use ::std::ffi::CString;
23
use ::std::fmt;
34
use ::std::str;
@@ -16,6 +17,7 @@ pub enum ErrorKind {
1617
None,
1718
Str(str::Utf8Error),
1819
Regex(regex::Error),
20+
Nul(ffi::NulError),
1921
}
2022

2123
impl Error {
@@ -29,7 +31,7 @@ impl Error {
2931
pub fn is_err(&self) -> bool {
3032
match self.kind {
3133
ErrorKind::None => false,
32-
ErrorKind::Str(_) | ErrorKind::Regex(_) => true,
34+
ErrorKind::Str(_) | ErrorKind::Regex(_) | ErrorKind::Nul(_) => true,
3335
}
3436
}
3537
}
@@ -40,6 +42,7 @@ impl fmt::Display for Error {
4042
ErrorKind::None => write!(f, "no error"),
4143
ErrorKind::Str(ref e) => e.fmt(f),
4244
ErrorKind::Regex(ref e) => e.fmt(f),
45+
ErrorKind::Nul(ref e) => e.fmt(f),
4346
}
4447
}
4548
}

regex-capi/src/rure.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,3 +570,62 @@ ffi_fn! {
570570
unsafe { (*re).len() }
571571
}
572572
}
573+
574+
ffi_fn! {
575+
fn rure_escape_must(pattern: *const c_char) -> *const c_char {
576+
let len = unsafe { CStr::from_ptr(pattern).to_bytes().len() };
577+
let pat = pattern as *const u8;
578+
let mut err = Error::new(ErrorKind::None);
579+
let esc = rure_escape(pat, len, &mut err);
580+
if err.is_err() {
581+
let _ = writeln!(&mut io::stderr(), "{}", err);
582+
let _ = writeln!(
583+
&mut io::stderr(), "aborting from rure_escape_must");
584+
unsafe { abort() }
585+
}
586+
esc
587+
}
588+
}
589+
590+
ffi_fn! {
591+
fn rure_escape(
592+
pattern: *const u8,
593+
length: size_t,
594+
error: *mut Error
595+
) -> *const c_char {
596+
let pat : &[u8] = unsafe{ slice::from_raw_parts(pattern, length) };
597+
let str_pat = match str::from_utf8(pat) {
598+
Ok(val) => val,
599+
Err(err) => {
600+
unsafe {
601+
if !error.is_null() {
602+
*error = Error::new(ErrorKind::Str(err));
603+
}
604+
return ptr::null();
605+
}
606+
}
607+
};
608+
let esc_pat = regex::escape(str_pat);
609+
let c_esc_pat = match CString::new(esc_pat) {
610+
Ok(val) => val,
611+
Err(err) => {
612+
unsafe {
613+
if !error.is_null() {
614+
*error = Error::new(ErrorKind::Nul(err));
615+
}
616+
return ptr::null();
617+
}
618+
}
619+
};
620+
c_esc_pat.into_raw() as *const c_char
621+
}
622+
}
623+
624+
ffi_fn! {
625+
fn rure_string_free(s: *mut c_char) {
626+
unsafe {
627+
if s.is_null() { return }
628+
CString::from_raw(s)
629+
};
630+
}
631+
}

0 commit comments

Comments
 (0)