1#![allow(
14 missing_docs,
15 non_snake_case,
16 non_camel_case_types,
17 non_upper_case_globals
18)]
19
20use core::ffi::c_void;
21use core::fmt;
22use core::ptr;
23use std::os::raw::c_int;
24
25use crate::abi::allocator::{xmlFree, xmlMalloc};
26use crate::abi::types::xmlChar;
27
28const MAX_NFA_STATES: usize = 1024;
34
35const MAX_PARSE_DEPTH: usize = 256;
37
38const REGEXP_MATCH: c_int = 1;
40
41const REGEXP_NOMATCH: c_int = 0;
43
44const REGEXP_ERROR: c_int = -1;
46
47#[derive(Debug, Clone, Copy, PartialEq)]
53enum AnchorType {
54 Start,
56 End,
58}
59
60#[derive(Debug, Clone, Copy, PartialEq)]
62enum PredefinedClass {
63 Digit,
65 NotDigit,
67 Space,
69 NotSpace,
71 Word,
73 NotWord,
75}
76
77#[derive(Debug, Clone, PartialEq)]
79enum Transition {
80 Char(u8),
82 Range(u8, u8),
84 Set(Vec<u8>),
86 NotRange(u8, u8),
88 NotSet(Vec<u8>),
90 Wildcard,
92 Predefined(PredefinedClass),
94 Epsilon,
96 Anchor(AnchorType),
98}
99
100#[derive(Debug, Clone)]
106struct NfaState {
107 transitions: Vec<(Transition, usize)>,
109 is_accept: bool,
111}
112
113impl NfaState {
114 fn new() -> Self {
115 NfaState {
116 transitions: Vec::new(),
117 is_accept: false,
118 }
119 }
120}
121
122#[derive(Debug, Clone)]
124struct Nfa {
125 states: Vec<NfaState>,
127 start: usize,
129}
130
131impl Nfa {
132 fn new() -> Self {
133 let start = 0;
134 Nfa {
135 states: vec![NfaState::new()],
136 start,
137 }
138 }
139
140 fn add_state(&mut self) -> usize {
142 let index = self.states.len();
143 self.states.push(NfaState::new());
144 index
145 }
146
147 fn add_transition(&mut self, from: usize, to: usize, trans: Transition) {
149 if from < self.states.len() && to < self.states.len() {
150 self.states[from].transitions.push((trans, to));
151 }
152 }
153
154 fn set_accept(&mut self, state: usize) {
156 if state < self.states.len() {
157 self.states[state].is_accept = true;
158 }
159 }
160}
161
162struct NfaFragment {
167 nfa: Nfa,
168 start: usize,
170 out: Vec<usize>,
175}
176
177impl NfaFragment {
178 fn new(nfa: Nfa, start: usize, out: Vec<usize>) -> Self {
179 NfaFragment { nfa, start, out }
180 }
181}
182
183impl Clone for NfaFragment {
184 fn clone(&self) -> Self {
185 NfaFragment {
186 nfa: self.nfa.clone(),
187 start: self.start,
188 out: self.out.clone(),
189 }
190 }
191}
192
193fn nfa_char(c: u8) -> NfaFragment {
199 let mut nfa = Nfa::new();
200 let start = nfa.start;
201 let accept = nfa.add_state();
202 nfa.set_accept(accept);
203 nfa.add_transition(start, accept, Transition::Char(c));
204 NfaFragment::new(nfa, start, vec![accept])
205}
206
207fn nfa_epsilon() -> NfaFragment {
209 let mut nfa = Nfa::new();
210 let start = nfa.start;
211 nfa.set_accept(start);
212 NfaFragment::new(nfa, start, vec![start])
213}
214
215fn nfa_range(lo: u8, hi: u8) -> NfaFragment {
217 let mut nfa = Nfa::new();
218 let start = nfa.start;
219 let accept = nfa.add_state();
220 nfa.set_accept(accept);
221 nfa.add_transition(start, accept, Transition::Range(lo, hi));
222 NfaFragment::new(nfa, start, vec![accept])
223}
224
225fn nfa_set(chars: Vec<u8>) -> NfaFragment {
227 let mut nfa = Nfa::new();
228 let start = nfa.start;
229 let accept = nfa.add_state();
230 nfa.set_accept(accept);
231 nfa.add_transition(start, accept, Transition::Set(chars));
232 NfaFragment::new(nfa, start, vec![accept])
233}
234
235fn nfa_not_range(lo: u8, hi: u8) -> NfaFragment {
237 let mut nfa = Nfa::new();
238 let start = nfa.start;
239 let accept = nfa.add_state();
240 nfa.set_accept(accept);
241 nfa.add_transition(start, accept, Transition::NotRange(lo, hi));
242 NfaFragment::new(nfa, start, vec![accept])
243}
244
245fn nfa_not_set(chars: Vec<u8>) -> NfaFragment {
247 let mut nfa = Nfa::new();
248 let start = nfa.start;
249 let accept = nfa.add_state();
250 nfa.set_accept(accept);
251 nfa.add_transition(start, accept, Transition::NotSet(chars));
252 NfaFragment::new(nfa, start, vec![accept])
253}
254
255fn nfa_wildcard() -> NfaFragment {
257 let mut nfa = Nfa::new();
258 let start = nfa.start;
259 let accept = nfa.add_state();
260 nfa.set_accept(accept);
261 nfa.add_transition(start, accept, Transition::Wildcard);
262 NfaFragment::new(nfa, start, vec![accept])
263}
264
265fn nfa_predefined(class: PredefinedClass) -> NfaFragment {
267 let mut nfa = Nfa::new();
268 let start = nfa.start;
269 let accept = nfa.add_state();
270 nfa.set_accept(accept);
271 nfa.add_transition(start, accept, Transition::Predefined(class));
272 NfaFragment::new(nfa, start, vec![accept])
273}
274
275fn nfa_start_anchor() -> NfaFragment {
277 let mut nfa = Nfa::new();
278 let start = nfa.start;
279 let accept = nfa.add_state();
280 nfa.set_accept(accept);
281 nfa.add_transition(start, accept, Transition::Anchor(AnchorType::Start));
282 NfaFragment::new(nfa, start, vec![accept])
283}
284
285fn nfa_end_anchor() -> NfaFragment {
287 let mut nfa = Nfa::new();
288 let start = nfa.start;
289 let accept = nfa.add_state();
290 nfa.set_accept(accept);
291 nfa.add_transition(start, accept, Transition::Anchor(AnchorType::End));
292 NfaFragment::new(nfa, start, vec![accept])
293}
294
295fn concat(a: NfaFragment, b: NfaFragment) -> NfaFragment {
300 let a_out = a.out.clone();
301 let a_start = a.start;
302 let a_size = a.nfa.states.len();
303
304 let mut nfa = a.nfa;
305 let b_start = b.nfa.start + a_size;
306
307 for mut state in b.nfa.states {
309 for (_, target) in &mut state.transitions {
310 *target += a_size;
311 }
312 nfa.states.push(state);
313 }
314
315 for &out_state in &a_out {
317 nfa.add_transition(out_state, b_start, Transition::Epsilon);
318 }
319
320 let b_out: Vec<usize> = b.out.iter().map(|&s| s + a_size).collect();
322
323 NfaFragment::new(nfa, a_start, b_out)
324}
325
326fn union(a: NfaFragment, b: NfaFragment) -> NfaFragment {
330 let mut nfa = Nfa::new();
331 let new_start = nfa.start;
332
333 let a_start = nfa.states.len();
335 let a_size = a.nfa.states.len();
336 for mut state in a.nfa.states {
337 for (_, target) in &mut state.transitions {
338 *target += a_start;
339 }
340 nfa.states.push(state);
341 }
342
343 let b_start = nfa.states.len();
345 for mut state in b.nfa.states {
346 for (_, target) in &mut state.transitions {
347 *target += b_start;
348 }
349 nfa.states.push(state);
350 }
351
352 nfa.add_transition(new_start, a_start, Transition::Epsilon);
354 nfa.add_transition(new_start, b_start, Transition::Epsilon);
355
356 let mut out = Vec::new();
358 for &s in &a.out {
359 out.push(s + a_start);
360 }
361 for &s in &b.out {
362 out.push(s + b_start);
363 }
364
365 NfaFragment::new(nfa, new_start, out)
366}
367
368fn kleene_star(frag: NfaFragment) -> NfaFragment {
370 let mut nfa = Nfa::new();
371 let new_start = nfa.start;
372 let new_accept = nfa.add_state();
373
374 let frag_start = nfa.states.len();
376 let frag_size = frag.nfa.states.len();
377 for mut state in frag.nfa.states {
378 for (_, target) in &mut state.transitions {
379 *target += frag_start;
380 }
381 nfa.states.push(state);
382 }
383
384 nfa.add_transition(new_start, new_accept, Transition::Epsilon);
386 nfa.add_transition(new_start, frag_start, Transition::Epsilon);
387
388 for &s in &frag.out {
390 nfa.add_transition(s + frag_start, frag_start, Transition::Epsilon);
391 nfa.add_transition(s + frag_start, new_accept, Transition::Epsilon);
392 }
393
394 nfa.set_accept(new_accept);
395 NfaFragment::new(nfa, new_start, vec![new_accept])
396}
397
398fn plus(frag: NfaFragment) -> NfaFragment {
400 let out_orig = frag.out.clone();
401 let frag_start_orig = frag.start;
402
403 let mut nfa = Nfa::new();
404 let new_start = nfa.start;
405
406 let frag_start = nfa.states.len();
408 for mut state in frag.nfa.states {
409 for (_, target) in &mut state.transitions {
410 *target += frag_start;
411 }
412 nfa.states.push(state);
413 }
414
415 nfa.add_transition(new_start, frag_start, Transition::Epsilon);
417
418 for &s in &out_orig {
420 let adjusted = s + frag_start;
421 nfa.add_transition(adjusted, frag_start, Transition::Epsilon);
422 }
423
424 let out: Vec<usize> = out_orig.iter().map(|&s| s + frag_start).collect();
426
427 NfaFragment::new(nfa, new_start, out)
428}
429
430fn optional(frag: NfaFragment) -> NfaFragment {
432 let mut nfa = Nfa::new();
433 let new_start = nfa.start;
434 let new_accept = nfa.add_state();
435
436 let frag_start = nfa.states.len();
437 for mut state in frag.nfa.states {
438 for (_, target) in &mut state.transitions {
439 *target += frag_start;
440 }
441 nfa.states.push(state);
442 }
443
444 nfa.add_transition(new_start, new_accept, Transition::Epsilon);
446 nfa.add_transition(new_start, frag_start, Transition::Epsilon);
447
448 for &s in &frag.out {
450 nfa.add_transition(s + frag_start, new_accept, Transition::Epsilon);
451 }
452
453 nfa.set_accept(new_accept);
454 NfaFragment::new(nfa, new_start, vec![new_accept])
455}
456
457#[derive(Debug, Clone, PartialEq)]
463enum RegexToken {
464 Char(u8),
466 Dot,
468 AnchorStart,
470 AnchorEnd,
472 LParen,
474 RParen,
476 Pipe,
478 Star,
480 Plus,
482 Question,
484 LBrace,
486 RBrace,
488 Comma,
490 Number(u32),
492 Escape(u8),
494 ClassStart,
496 ClassEnd,
498 ClassNegate,
500 ClassRange,
502}
503
504struct RegexParser<'a> {
506 input: &'a [u8],
508 pos: usize,
510 lookahead: Option<RegexToken>,
512}
513
514impl<'a> RegexParser<'a> {
515 fn new(input: &'a [u8]) -> Self {
516 RegexParser {
517 input,
518 pos: 0,
519 lookahead: None,
520 }
521 }
522
523 fn peek(&self) -> Option<u8> {
525 self.input.get(self.pos).copied()
526 }
527
528 fn advance(&mut self) -> Option<u8> {
530 let ch = self.input.get(self.pos).copied();
531 if ch.is_some() {
532 self.pos += 1;
533 }
534 ch
535 }
536
537 fn skip_whitespace(&mut self) {
539 while let Some(ch) = self.peek() {
540 if ch == b' ' || ch == b'\t' || ch == b'\n' || ch == b'\r' {
541 self.advance();
542 } else {
543 break;
544 }
545 }
546 }
547
548 fn scan_token(&mut self) -> Option<RegexToken> {
550 let ch = self.advance()?;
551 match ch {
552 b'.' => Some(RegexToken::Dot),
553 b'^' => Some(RegexToken::AnchorStart),
554 b'$' => Some(RegexToken::AnchorEnd),
555 b'(' => Some(RegexToken::LParen),
556 b')' => Some(RegexToken::RParen),
557 b'|' => Some(RegexToken::Pipe),
558 b'*' => Some(RegexToken::Star),
559 b'+' => Some(RegexToken::Plus),
560 b'?' => Some(RegexToken::Question),
561 b'{' => Some(RegexToken::LBrace),
562 b'}' => Some(RegexToken::RBrace),
563 b',' => Some(RegexToken::Comma),
564 b'[' => Some(RegexToken::ClassStart),
565 b']' => Some(RegexToken::ClassEnd),
566 b'\\' => {
567 let next = self.advance()?;
569 Some(RegexToken::Escape(next))
570 }
571 _ => Some(RegexToken::Char(ch)),
572 }
573 }
574
575 fn next_token(&mut self) -> Option<RegexToken> {
577 if let Some(token) = self.lookahead.take() {
578 Some(token)
579 } else {
580 self.scan_token()
581 }
582 }
583
584 fn unscan(&mut self, token: RegexToken) {
586 self.lookahead = Some(token);
587 }
588
589 fn parse(&mut self) -> Result<NfaFragment, String> {
593 let frag = self.parse_alternation()?;
594 Ok(frag)
595 }
596
597 fn parse_alternation(&mut self) -> Result<NfaFragment, String> {
599 let mut frag = self.parse_sequence()?;
600
601 loop {
602 match self.next_token() {
603 Some(RegexToken::Pipe) => {
604 let rhs = self.parse_sequence()?;
605 frag = union(frag, rhs);
606 }
607 Some(other) => {
608 self.unscan(other);
609 break;
610 }
611 None => break,
612 }
613 }
614
615 Ok(frag)
616 }
617
618 fn parse_sequence(&mut self) -> Result<NfaFragment, String> {
620 let mut fragments: Vec<NfaFragment> = Vec::new();
621
622 loop {
623 match self.peek() {
624 None => break,
625 Some(b'|') | Some(b')') => break,
626 _ => {}
627 }
628
629 let atom = self.parse_atom()?;
630 fragments.push(atom);
631 }
632
633 if fragments.is_empty() {
634 return Ok(nfa_epsilon());
635 }
636
637 let mut result = fragments.remove(0);
638 for frag in fragments {
639 result = concat(result, frag);
640 }
641
642 Ok(result)
643 }
644
645 fn parse_atom(&mut self) -> Result<NfaFragment, String> {
647 let token = self
648 .next_token()
649 .ok_or_else(|| "Unexpected end of pattern".to_string())?;
650
651 let base = match token {
652 RegexToken::Char(c) => nfa_char(c),
653 RegexToken::Dot => nfa_wildcard(),
654 RegexToken::AnchorStart => nfa_start_anchor(),
655 RegexToken::AnchorEnd => nfa_end_anchor(),
656 RegexToken::LParen => {
657 let inner = self.parse_alternation()?;
658 match self.next_token() {
659 Some(RegexToken::RParen) => inner,
660 Some(t) => return Err(format!("Expected ')', got {:?}", t)),
661 None => return Err("Unterminated group".to_string()),
662 }
663 }
664 RegexToken::Escape(c) => {
665 match c {
666 b'd' => nfa_predefined(PredefinedClass::Digit),
667 b'D' => nfa_predefined(PredefinedClass::NotDigit),
668 b's' => nfa_predefined(PredefinedClass::Space),
669 b'S' => nfa_predefined(PredefinedClass::NotSpace),
670 b'w' => nfa_predefined(PredefinedClass::Word),
671 b'W' => nfa_predefined(PredefinedClass::NotWord),
672 b'n' => nfa_char(b'\n'),
673 b'r' => nfa_char(b'\r'),
674 b't' => nfa_char(b'\t'),
675 b'\\' => nfa_char(b'\\'),
676 b'.' => nfa_char(b'.'),
677 b'^' => nfa_char(b'^'),
678 b'$' => nfa_char(b'$'),
679 b'|' => nfa_char(b'|'),
680 b'*' => nfa_char(b'*'),
681 b'+' => nfa_char(b'+'),
682 b'?' => nfa_char(b'?'),
683 b'(' => nfa_char(b'('),
684 b')' => nfa_char(b')'),
685 b'[' => nfa_char(b'['),
686 b']' => nfa_char(b']'),
687 b'{' => nfa_char(b'{'),
688 b'}' => nfa_char(b'}'),
689 b'-' => nfa_char(b'-'),
690 b'0'..=b'9' => {
691 nfa_char(c)
693 }
694 _ => nfa_char(c),
695 }
696 }
697 RegexToken::ClassStart => self.parse_char_class()?,
698 _ => return Err(format!("Unexpected token: {:?}", token)),
699 };
700
701 self.parse_quantifier(base)
703 }
704
705 fn parse_quantifier(&mut self, frag: NfaFragment) -> Result<NfaFragment, String> {
707 match self.peek() {
708 Some(b'*') => {
709 self.advance();
710 Ok(kleene_star(frag))
711 }
712 Some(b'+') => {
713 self.advance();
714 Ok(plus(frag))
715 }
716 Some(b'?') => {
717 self.advance();
718 Ok(optional(frag))
719 }
720 Some(b'{') => {
721 self.advance();
722 self.parse_brace_quantifier(frag)
723 }
724 _ => Ok(frag),
725 }
726 }
727
728 fn parse_brace_quantifier(&mut self, frag: NfaFragment) -> Result<NfaFragment, String> {
730 let mut min: u32 = 0;
732 while let Some(b'0'..=b'9') = self.peek() {
733 let d = self.advance().unwrap() - b'0';
734 min = min * 10 + d as u32;
735 }
736
737 let mut max: Option<u32> = None;
738
739 match self.peek() {
740 Some(b',') => {
741 self.advance();
742 let mut max_val: u32 = 0;
744 let mut has_max = false;
745 while let Some(b'0'..=b'9') = self.peek() {
746 let d = self.advance().unwrap() - b'0';
747 max_val = max_val * 10 + d as u32;
748 has_max = true;
749 }
750 if has_max {
751 max = Some(max_val);
752 }
753 }
754 Some(b'}') => {
755 max = Some(min);
756 }
757 _ => return Err("Expected '}' in quantifier".to_string()),
758 }
759
760 match self.peek() {
762 Some(b'}') => {
763 self.advance();
764 }
765 _ => return Err("Expected '}' in quantifier".to_string()),
766 }
767
768 if min == 0 && max.is_none() {
773 Ok(kleene_star(frag))
775 } else if min == 0 && max == Some(0) {
776 Ok(nfa_epsilon())
778 } else if min == 1 && max.is_none() {
779 Ok(plus(frag))
781 } else if min == 0 && max == Some(1) {
782 Ok(optional(frag))
784 } else {
785 let mut result = nfa_epsilon();
788 for _ in 0..min {
789 result = concat(result, frag.clone());
790 }
791 if let Some(max_val) = max {
792 for _ in min..max_val {
793 result = concat(result, optional(frag.clone()));
794 }
795 } else {
796 result = concat(result, kleene_star(frag.clone()));
798 }
799 Ok(result)
800 }
801 }
802
803 fn parse_char_class(&mut self) -> Result<NfaFragment, String> {
805 let mut chars: Vec<u8> = Vec::new();
806 let mut ranges: Vec<(u8, u8)> = Vec::new();
807 let mut negated = false;
808
809 if let Some(b'^') = self.peek() {
811 negated = true;
812 self.advance();
813 }
814
815 let mut prev: Option<u8> = None;
817
818 loop {
819 match self.peek() {
820 None => return Err("Unterminated character class".to_string()),
821 Some(b']') => {
822 if prev.is_some() {
823 if let Some(c) = prev {
825 chars.push(c);
826 }
827 prev = None;
828 }
829 self.advance();
830 break;
831 }
832 Some(b'-') if prev.is_some() => {
833 self.advance();
835 let lo = prev.take().unwrap();
836 match self.peek() {
837 Some(b']') => {
838 chars.push(lo);
840 chars.push(b'-');
841 prev = None;
842 }
843 Some(ch) => {
844 self.advance();
845 if lo <= ch {
846 ranges.push((lo, ch));
847 }
848 prev = None;
849 }
850 None => {
851 chars.push(lo);
852 chars.push(b'-');
853 prev = None;
854 }
855 }
856 }
857 Some(b'\\') => {
858 self.advance();
859 if let Some(ch) = self.advance() {
860 match ch {
864 b'd' | b'D' | b's' | b'S' | b'w' | b'W' => {
865 if let Some(p) = prev.take() {
867 chars.push(p);
868 }
869 let class = match ch {
873 b'd' => PredefinedClass::Digit,
874 b'D' => PredefinedClass::NotDigit,
875 b's' => PredefinedClass::Space,
876 b'S' => PredefinedClass::NotSpace,
877 b'w' => PredefinedClass::Word,
878 b'W' => PredefinedClass::NotWord,
879 _ => unreachable!(),
880 };
881 match class {
886 PredefinedClass::Digit => {
887 ranges.push((b'0', b'9'));
888 }
889 PredefinedClass::NotDigit => {
890 ranges.push((0x00, b'/' - 1));
891 ranges.push((b':', 0xFF));
892 }
893 PredefinedClass::Space => {
894 chars.push(b' ');
895 chars.push(b'\t');
896 chars.push(b'\n');
897 chars.push(b'\r');
898 }
899 PredefinedClass::NotSpace => {
900 ranges.push((0x00, b' ' - 1));
902 ranges.push((b'!' + 1, b'\t' - 1));
903 ranges.push((b'\t' + 1, b'\n' - 1));
904 ranges.push((b'\n' + 1, b'\r' - 1));
905 ranges.push((b'\r' + 1, 0xFF));
906 }
907 PredefinedClass::Word => {
908 ranges.push((b'0', b'9'));
909 ranges.push((b'A', b'Z'));
910 ranges.push((b'a', b'z'));
911 chars.push(b'_');
912 }
913 PredefinedClass::NotWord => {
914 ranges.push((0x00, b'0' - 1));
915 ranges.push((b'9' + 1, b'A' - 1));
916 ranges.push((b'Z' + 1, b'_' - 1));
917 ranges.push((b'_' + 1, b'a' - 1));
918 ranges.push((b'z' + 1, 0xFF));
919 }
920 }
921 }
922 _ => {
923 let c = match ch {
924 b'n' => b'\n',
925 b'r' => b'\r',
926 b't' => b'\t',
927 b'\\' => b'\\',
928 b'0'..=b'9' => ch, _ => ch,
930 };
931 if let Some(p) = prev.take() {
932 chars.push(p);
933 }
934 prev = Some(c);
935 }
936 }
937 }
938 }
939 Some(ch) => {
940 self.advance();
941 if let Some(p) = prev.take() {
942 chars.push(p);
943 }
944 prev = Some(ch);
945 }
946 }
947 }
948
949 if let Some(c) = prev {
951 chars.push(c);
952 }
953
954 let mut combined_ranges: Vec<(u8, u8)> = ranges;
956 for &c in &chars {
957 combined_ranges.push((c, c));
958 }
959
960 if combined_ranges.is_empty() {
961 return if negated {
962 Ok(nfa_epsilon())
964 } else {
965 Ok(nfa_epsilon())
967 };
968 }
969
970 combined_ranges.sort_by(|a, b| a.0.cmp(&b.0));
972 let mut merged: Vec<(u8, u8)> = Vec::new();
973 for (lo, hi) in combined_ranges {
974 if let Some(last) = merged.last_mut() {
975 if lo <= last.1 + 1 {
976 last.1 = last.1.max(hi);
977 continue;
978 }
979 }
980 merged.push((lo, hi));
981 }
982
983 if negated {
985 if merged.len() == 1 && merged[0].0 == 0 && merged[0].1 == 255 {
986 Ok(nfa_epsilon())
988 } else {
989 Ok(nfa_not_range(0, 255))
990 }
991 } else if merged.len() == 1 {
992 let (lo, hi) = merged[0];
993 if lo == hi {
994 Ok(nfa_char(lo))
995 } else {
996 Ok(nfa_range(lo, hi))
997 }
998 } else {
999 let mut result = nfa_range(merged[0].0, merged[0].1);
1001 for &(lo, hi) in &merged[1..] {
1002 result = union(result, nfa_range(lo, hi));
1003 }
1004 Ok(result)
1005 }
1006 }
1007}
1008
1009fn matches_transition(c: u8, trans: &Transition) -> bool {
1015 match trans {
1016 Transition::Char(ch) => c == *ch,
1017 Transition::Range(lo, hi) => c >= *lo && c <= *hi,
1018 Transition::Set(chars) => chars.contains(&c),
1019 Transition::NotRange(lo, hi) => c < *lo || c > *hi,
1020 Transition::NotSet(chars) => !chars.contains(&c),
1021 Transition::Wildcard => true,
1022 Transition::Predefined(class) => matches_predefined(c, *class),
1023 Transition::Epsilon => false, Transition::Anchor(_) => false, }
1026}
1027
1028fn matches_predefined(c: u8, class: PredefinedClass) -> bool {
1030 match class {
1031 PredefinedClass::Digit => c >= b'0' && c <= b'9',
1032 PredefinedClass::NotDigit => c < b'0' || c > b'9',
1033 PredefinedClass::Space => {
1034 c == b' ' || c == b'\t' || c == b'\n' || c == b'\r' || c == 0x0b || c == 0x0c
1035 }
1036 PredefinedClass::NotSpace => {
1037 !(c == b' ' || c == b'\t' || c == b'\n' || c == b'\r' || c == 0x0b || c == 0x0c)
1038 }
1039 PredefinedClass::Word => {
1040 (c >= b'0' && c <= b'9')
1041 || (c >= b'A' && c <= b'Z')
1042 || (c >= b'a' && c <= b'z')
1043 || c == b'_'
1044 }
1045 PredefinedClass::NotWord => {
1046 !((c >= b'0' && c <= b'9')
1047 || (c >= b'A' && c <= b'Z')
1048 || (c >= b'a' && c <= b'z')
1049 || c == b'_')
1050 }
1051 }
1052}
1053
1054fn epsilon_closure(nfa: &Nfa, states: &[usize]) -> Vec<usize> {
1056 let mut visited = vec![false; nfa.states.len()];
1057 let mut result = Vec::new();
1058 let mut stack: Vec<usize> = states.to_vec();
1059
1060 while let Some(s) = stack.pop() {
1061 if s >= nfa.states.len() || visited[s] {
1062 continue;
1063 }
1064 visited[s] = true;
1065 result.push(s);
1066
1067 for (cond, target) in &nfa.states[s].transitions {
1068 if matches!(cond, Transition::Epsilon) {
1069 if *target < nfa.states.len() && !visited[*target] {
1070 stack.push(*target);
1071 }
1072 }
1073 }
1074 }
1075
1076 result
1077}
1078
1079fn move_on_char(nfa: &Nfa, states: &[usize], c: u8, is_start: bool, is_end: bool) -> Vec<usize> {
1084 let mut expanded = states.to_vec();
1087 let mut more = true;
1088 while more {
1089 more = false;
1090 let mut new_states = Vec::new();
1091 for &s in &expanded {
1092 if s >= nfa.states.len() {
1093 continue;
1094 }
1095 for (cond, target) in &nfa.states[s].transitions {
1096 if *target >= nfa.states.len() {
1097 continue;
1098 }
1099 let should_follow = match cond {
1100 Transition::Anchor(AnchorType::Start) => {
1101 is_start && !expanded.contains(target) && !new_states.contains(target)
1102 }
1103 Transition::Anchor(AnchorType::End) => {
1104 is_end && !expanded.contains(target) && !new_states.contains(target)
1105 }
1106 _ => false,
1107 };
1108 if should_follow {
1109 new_states.push(*target);
1110 more = true;
1111 }
1112 }
1113 }
1114 expanded.extend(new_states);
1115 }
1116
1117 let mut eps_expanded = expanded.clone();
1121 let mut more_eps = true;
1122 while more_eps {
1123 more_eps = false;
1124 let mut new_eps = Vec::new();
1125 for &s in &eps_expanded {
1126 if s >= nfa.states.len() {
1127 continue;
1128 }
1129 for (cond, target) in &nfa.states[s].transitions {
1130 if let Transition::Epsilon = cond {
1131 if *target < nfa.states.len()
1132 && !eps_expanded.contains(target)
1133 && !new_eps.contains(target)
1134 {
1135 new_eps.push(*target);
1136 more_eps = true;
1137 }
1138 }
1139 }
1140 }
1141 eps_expanded.extend(new_eps);
1142 }
1143
1144 let mut next = Vec::new();
1146 for &s in &eps_expanded {
1147 if s >= nfa.states.len() {
1148 continue;
1149 }
1150 for (cond, target) in &nfa.states[s].transitions {
1151 if *target >= nfa.states.len() {
1152 continue;
1153 }
1154 match cond {
1155 Transition::Epsilon => continue,
1156 Transition::Anchor(_) => continue, _ => {
1158 if matches_transition(c, cond) {
1159 if !next.contains(target) {
1160 next.push(*target);
1161 }
1162 }
1163 }
1164 }
1165 }
1166 }
1167
1168 next
1169}
1170
1171fn has_accept_state(nfa: &Nfa, states: &[usize]) -> bool {
1173 states
1174 .iter()
1175 .any(|&s| s < nfa.states.len() && nfa.states[s].is_accept)
1176}
1177
1178fn nfa_exec(nfa: &Nfa, input: &[u8]) -> c_int {
1183 if nfa.states.is_empty() {
1184 return REGEXP_ERROR;
1185 }
1186
1187 let mut current = epsilon_closure(nfa, &[nfa.start]);
1189
1190 if input.is_empty() {
1193 let mut final_states = current.clone();
1194 for &s in ¤t {
1195 for (cond, target) in &nfa.states[s].transitions {
1196 if let Transition::Anchor(AnchorType::End) = cond {
1197 if *target < nfa.states.len() {
1198 let ec = epsilon_closure(nfa, &[*target]);
1199 final_states.extend(ec);
1200 }
1201 }
1202 }
1203 }
1204 final_states = epsilon_closure(nfa, &final_states);
1205 return if has_accept_state(nfa, &final_states) {
1206 REGEXP_MATCH
1207 } else {
1208 REGEXP_NOMATCH
1209 };
1210 }
1211
1212 for (i, &c) in input.iter().enumerate() {
1213 let is_start = i == 0;
1214 let next_states = move_on_char(nfa, ¤t, c, is_start, false);
1217 if next_states.is_empty() {
1218 return REGEXP_NOMATCH;
1219 }
1220
1221 current = epsilon_closure(nfa, &next_states);
1222 if current.is_empty() {
1223 return REGEXP_NOMATCH;
1224 }
1225 }
1226
1227 let mut final_states = current.clone();
1231 for &s in ¤t {
1232 for (cond, target) in &nfa.states[s].transitions {
1233 if let Transition::Anchor(AnchorType::End) = cond {
1234 if *target < nfa.states.len() {
1235 let ec = epsilon_closure(nfa, &[*target]);
1236 final_states.extend(ec);
1237 }
1238 }
1239 }
1240 }
1241 final_states = epsilon_closure(nfa, &final_states);
1242
1243 if has_accept_state(nfa, &final_states) {
1244 REGEXP_MATCH
1245 } else {
1246 REGEXP_NOMATCH
1247 }
1248}
1249
1250#[repr(C)]
1260pub struct XmlRegexp {
1261 pattern: *mut xmlChar,
1263 nfa: Option<Box<Nfa>>,
1265 is_deterministic: c_int,
1267}
1268
1269fn compile_pattern(pattern: &[u8]) -> Option<Box<Nfa>> {
1271 if pattern.is_empty() {
1272 let mut nfa = Nfa::new();
1274 nfa.set_accept(nfa.start);
1275 return Some(Box::new(nfa));
1276 }
1277
1278 let mut parser = RegexParser::new(pattern);
1279 match parser.parse() {
1280 Ok(fragment) => {
1281 let mut nfa = fragment.nfa;
1287 for state in &mut nfa.states {
1288 state.is_accept = false;
1289 }
1290 for &s in &fragment.out {
1291 if s < nfa.states.len() {
1292 nfa.set_accept(s);
1293 }
1294 }
1295 Some(Box::new(nfa))
1296 }
1297 Err(_) => None,
1298 }
1299}
1300
1301fn is_deterministic(nfa: &Nfa) -> bool {
1303 for (state_idx, state) in nfa.states.iter().enumerate() {
1324 let epsilon_count = state
1326 .transitions
1327 .iter()
1328 .filter(|(cond, _)| matches!(cond, Transition::Epsilon))
1329 .count();
1330
1331 let consuming_count = state
1333 .transitions
1334 .iter()
1335 .filter(|(cond, _)| !matches!(cond, Transition::Epsilon | Transition::Anchor(_)))
1336 .count();
1337
1338 if epsilon_count > 1 {
1340 return false;
1341 }
1342
1343 if epsilon_count > 0 && consuming_count > 0 {
1345 return false;
1346 }
1347
1348 let closure = if epsilon_count == 0 {
1351 epsilon_closure(nfa, &[state_idx])
1352 } else {
1353 continue;
1355 };
1356
1357 let mut has_wildcard = false;
1358 let mut chars = Vec::new();
1359 let mut has_range = false;
1360 let mut has_not = false;
1361 let mut has_predefined = false;
1362
1363 for &s_idx in &closure {
1364 if s_idx >= nfa.states.len() {
1365 continue;
1366 }
1367 for (cond, _) in &nfa.states[s_idx].transitions {
1368 match cond {
1369 Transition::Epsilon | Transition::Anchor(_) => {}
1370 Transition::Wildcard => {
1371 if has_wildcard {
1372 return false;
1373 }
1374 has_wildcard = true;
1375 }
1376 Transition::Char(c) => {
1377 chars.push(*c);
1378 }
1379 Transition::Range(_, _) => {
1380 has_range = true;
1381 }
1382 Transition::Set(_) => {}
1383 Transition::NotRange(_, _) | Transition::NotSet(_) => {
1384 has_not = true;
1385 }
1386 Transition::Predefined(_) => {
1387 has_predefined = true;
1388 }
1389 }
1390 }
1391 }
1392
1393 if has_wildcard && (!chars.is_empty() || has_range || has_not || has_predefined) {
1395 return false;
1396 }
1397
1398 if has_range && (has_wildcard || has_not || has_predefined) {
1399 return false;
1400 }
1401
1402 if has_not && (has_wildcard || has_range || has_predefined) {
1403 return false;
1404 }
1405
1406 if has_predefined && (has_wildcard || has_range || has_not) {
1407 return false;
1408 }
1409
1410 chars.sort();
1412 let dedup_len = {
1413 chars.dedup();
1414 chars.len()
1415 };
1416 if dedup_len > 1 && (has_range || has_wildcard || has_not || has_predefined) {
1425 return false;
1426 }
1427 }
1428
1429 true
1430}
1431
1432unsafe fn xml_strlen(s: *const xmlChar) -> usize {
1438 if s.is_null() {
1439 return 0;
1440 }
1441 let mut len: usize = 0;
1442 while *s.add(len) != 0 {
1443 len += 1;
1444 }
1445 len
1446}
1447
1448unsafe fn xml_strdup(s: *const xmlChar) -> *mut xmlChar {
1450 if s.is_null() {
1451 return ptr::null_mut();
1452 }
1453 let len = xml_strlen(s);
1454 let new_ptr = xmlMalloc((len + 1) * core::mem::size_of::<xmlChar>()) as *mut xmlChar;
1455 if new_ptr.is_null() {
1456 return ptr::null_mut();
1457 }
1458 for i in 0..=len {
1459 unsafe { *new_ptr.add(i) = *s.add(i) };
1460 }
1461 new_ptr
1462}
1463
1464#[no_mangle]
1478pub unsafe extern "C" fn xmlRegexpCompile(pattern: *const xmlChar) -> *mut XmlRegexp {
1479 if pattern.is_null() {
1480 return ptr::null_mut();
1481 }
1482
1483 let len = xml_strlen(pattern);
1484 let pattern_bytes = unsafe { core::slice::from_raw_parts(pattern, len) };
1485
1486 let nfa = compile_pattern(pattern_bytes);
1487 let pattern_copy = xml_strdup(pattern);
1488
1489 let compiled = xmlMalloc(core::mem::size_of::<XmlRegexp>()) as *mut XmlRegexp;
1490 if compiled.is_null() {
1491 if !pattern_copy.is_null() {
1492 xmlFree(pattern_copy as *mut c_void);
1493 }
1494 return ptr::null_mut();
1495 }
1496
1497 let det = nfa
1498 .as_ref()
1499 .map_or(0, |n| if is_deterministic(n) { 1 } else { 0 });
1500
1501 unsafe {
1502 (*compiled).pattern = pattern_copy;
1503 core::ptr::write(&mut (*compiled).nfa, nfa);
1507 (*compiled).is_deterministic = det;
1508 }
1509
1510 compiled
1511}
1512
1513#[no_mangle]
1528pub unsafe extern "C" fn xmlRegexpExec(compiled: *const XmlRegexp, value: *const xmlChar) -> c_int {
1529 if compiled.is_null() || value.is_null() {
1530 return REGEXP_ERROR;
1531 }
1532
1533 let regex = unsafe { &*compiled };
1534 let nfa = match ®ex.nfa {
1535 Some(nfa) => nfa,
1536 None => return REGEXP_ERROR,
1537 };
1538
1539 let len = xml_strlen(value);
1540 let input = unsafe { core::slice::from_raw_parts(value, len) };
1541
1542 nfa_exec(nfa, input)
1543}
1544
1545#[no_mangle]
1559pub unsafe extern "C" fn xmlRegexpIsDeterministic(compiled: *const XmlRegexp) -> c_int {
1560 if compiled.is_null() {
1561 return 0;
1562 }
1563 unsafe { (*compiled).is_deterministic }
1564}
1565
1566#[no_mangle]
1578pub unsafe extern "C" fn xmlRegexpPrint(compiled: *const XmlRegexp) {
1579 if compiled.is_null() {
1580 eprintln!("(null regex)");
1581 return;
1582 }
1583
1584 let regex = unsafe { &*compiled };
1585 let pattern_str = if regex.pattern.is_null() {
1586 "(null)"
1587 } else {
1588 let len = xml_strlen(regex.pattern);
1589 let slice = unsafe { core::slice::from_raw_parts(regex.pattern, len) };
1590 core::str::from_utf8(slice).unwrap_or("(invalid utf-8)")
1591 };
1592
1593 eprintln!("Regex: /{}/", pattern_str);
1594 eprintln!(" deterministic: {}", regex.is_deterministic);
1595
1596 if let Some(ref nfa) = regex.nfa {
1597 eprintln!(" states: {}", nfa.states.len());
1598 eprintln!(" start state: {}", nfa.start);
1599 for (i, state) in nfa.states.iter().enumerate() {
1600 eprint!(" state[{}]: ", i);
1601 if state.is_accept {
1602 eprint!("(accept) ");
1603 }
1604 for (j, (cond, target)) in state.transitions.iter().enumerate() {
1605 if j > 0 {
1606 eprint!(", ");
1607 }
1608 match cond {
1609 Transition::Epsilon => eprint!("ε->{}", target),
1610 Transition::Char(c) => {
1611 if *c >= 0x20 && *c <= 0x7e {
1612 eprint!("'{}'->{}", *c as char, target);
1613 } else {
1614 eprint!("0x{:02x}->{}", c, target);
1615 }
1616 }
1617 Transition::Range(lo, hi) => {
1618 eprint!("[{:02x}-{:02x}]->{}", lo, hi, target);
1619 }
1620 Transition::Set(chars) => {
1621 eprint!("{{");
1622 for (k, c) in chars.iter().enumerate() {
1623 if k > 0 {
1624 eprint!(",");
1625 }
1626 eprint!("0x{:02x}", c);
1627 }
1628 eprint!("}}->{}", target);
1629 }
1630 Transition::NotRange(lo, hi) => {
1631 eprint!("[^{:02x}-{:02x}]->{}", lo, hi, target);
1632 }
1633 Transition::NotSet(chars) => {
1634 eprint!("^{{");
1635 for (k, c) in chars.iter().enumerate() {
1636 if k > 0 {
1637 eprint!(",");
1638 }
1639 eprint!("0x{:02x}", c);
1640 }
1641 eprint!("}}->{}", target);
1642 }
1643 Transition::Wildcard => eprint!(".*->{}", target),
1644 Transition::Predefined(class) => {
1645 let name = match class {
1646 PredefinedClass::Digit => "\\d",
1647 PredefinedClass::NotDigit => "\\D",
1648 PredefinedClass::Space => "\\s",
1649 PredefinedClass::NotSpace => "\\S",
1650 PredefinedClass::Word => "\\w",
1651 PredefinedClass::NotWord => "\\W",
1652 };
1653 eprint!("{}->{}", name, target);
1654 }
1655 Transition::Anchor(at) => match at {
1656 AnchorType::Start => eprint!("^->{}", target),
1657 AnchorType::End => eprint!("$->{}", target),
1658 },
1659 }
1660 }
1661 eprintln!();
1662 }
1663 }
1664}
1665
1666#[no_mangle]
1679pub unsafe extern "C" fn xmlRegFreeRegexp(regexp: *mut XmlRegexp) {
1680 if regexp.is_null() {
1681 return;
1682 }
1683 unsafe {
1684 if !(*regexp).pattern.is_null() {
1685 xmlFree((*regexp).pattern as *mut c_void);
1686 }
1687 let _ = (*regexp).nfa.take();
1689 xmlFree(regexp as *mut c_void);
1690 }
1691}
1692
1693#[repr(C)]
1703pub struct RegExecCtxt {
1704 compiled: *mut XmlRegexp,
1706 current_states: Vec<usize>,
1708 started: bool,
1710}
1711
1712#[no_mangle]
1724pub unsafe extern "C" fn xmlRegNewExecCtxt(
1725 compiled: *mut XmlRegexp,
1726 _data: *mut c_void,
1727) -> *mut RegExecCtxt {
1728 if compiled.is_null() {
1729 return ptr::null_mut();
1730 }
1731
1732 let ctxt = xmlMalloc(core::mem::size_of::<RegExecCtxt>()) as *mut RegExecCtxt;
1733 if ctxt.is_null() {
1734 return ptr::null_mut();
1735 }
1736
1737 unsafe {
1738 (*ctxt).compiled = compiled;
1739 core::ptr::write(&mut (*ctxt).current_states, Vec::new());
1743 (*ctxt).started = false;
1744 }
1745
1746 ctxt
1747}
1748
1749#[no_mangle]
1765pub unsafe extern "C" fn xmlRegExecPushString(
1766 ctxt: *mut RegExecCtxt,
1767 value: *const xmlChar,
1768) -> c_int {
1769 if ctxt.is_null() {
1770 return REGEXP_ERROR;
1771 }
1772
1773 let exec_ctxt = unsafe { &mut *ctxt };
1774 let regex = match unsafe { exec_ctxt.compiled.as_mut() } {
1775 Some(r) => r,
1776 None => return REGEXP_ERROR,
1777 };
1778
1779 let nfa = match ®ex.nfa {
1780 Some(nfa) => nfa,
1781 None => return REGEXP_ERROR,
1782 };
1783
1784 if value.is_null() {
1785 if !exec_ctxt.started {
1788 exec_ctxt.current_states = epsilon_closure(nfa, &[nfa.start]);
1789 exec_ctxt.started = true;
1790 }
1791 return if has_accept_state(nfa, &exec_ctxt.current_states) {
1792 REGEXP_MATCH
1793 } else {
1794 REGEXP_NOMATCH
1795 };
1796 }
1797
1798 let len = xml_strlen(value);
1799 let input = unsafe { core::slice::from_raw_parts(value, len) };
1800
1801 if input.is_empty() {
1802 return REGEXP_NOMATCH;
1803 }
1804
1805 if !exec_ctxt.started {
1806 exec_ctxt.current_states = epsilon_closure(nfa, &[nfa.start]);
1808 exec_ctxt.started = true;
1809 }
1810
1811 for (i, &c) in input.iter().enumerate() {
1812 let is_end = i == input.len() - 1 && true; let next_states = move_on_char(nfa, &exec_ctxt.current_states, c, i == 0, is_end);
1814 if next_states.is_empty() {
1815 exec_ctxt.current_states = Vec::new();
1816 return REGEXP_NOMATCH;
1817 }
1818 exec_ctxt.current_states = epsilon_closure(nfa, &next_states);
1819 }
1820
1821 if has_accept_state(nfa, &exec_ctxt.current_states) {
1823 REGEXP_MATCH
1824 } else {
1825 REGEXP_NOMATCH
1826 }
1827}
1828
1829#[no_mangle]
1842pub unsafe extern "C" fn xmlRegFreeExecCtxt(ctxt: *mut RegExecCtxt) {
1843 if ctxt.is_null() {
1844 return;
1845 }
1846 unsafe {
1847 core::ptr::drop_in_place(&mut (*ctxt).current_states);
1850 xmlFree(ctxt as *mut c_void);
1851 }
1852}
1853
1854#[cfg(test)]
1859mod tests {
1860 use super::*;
1861 use core::ptr;
1862
1863 fn to_xml_str(s: &[u8]) -> *mut xmlChar {
1868 let len = s.len();
1869 let ptr = unsafe { xmlMalloc((len + 1) * core::mem::size_of::<xmlChar>()) } as *mut xmlChar;
1870 if ptr.is_null() {
1871 return ptr::null_mut();
1872 }
1873 unsafe {
1874 core::ptr::copy_nonoverlapping(s.as_ptr(), ptr, len);
1875 *ptr.add(len) = 0;
1876 }
1877 ptr
1878 }
1879
1880 fn match_regex(pattern: &[u8], input: &[u8]) -> c_int {
1882 let pat = to_xml_str(pattern);
1883 let val = to_xml_str(input);
1884 unsafe {
1885 let compiled = xmlRegexpCompile(pat);
1886 if compiled.is_null() {
1887 return REGEXP_ERROR;
1888 }
1889 let ret = xmlRegexpExec(compiled, val);
1890 xmlRegFreeRegexp(compiled);
1891 ret
1892 }
1893 }
1894
1895 fn compile(pattern: &[u8]) -> *mut XmlRegexp {
1897 let pat = to_xml_str(pattern);
1898 unsafe { xmlRegexpCompile(pat) }
1899 }
1900
1901 #[test]
1904 fn test_literal_exact() {
1905 assert_eq!(match_regex(b"hello", b"hello"), REGEXP_MATCH);
1906 }
1907
1908 #[test]
1909 fn test_literal_no_match() {
1910 assert_eq!(match_regex(b"hello", b"world"), REGEXP_NOMATCH);
1911 }
1912
1913 #[test]
1914 fn test_literal_partial_prefix() {
1915 assert_eq!(match_regex(b"hello", b"hel"), REGEXP_NOMATCH);
1917 }
1918
1919 #[test]
1920 fn test_literal_empty_pattern() {
1921 assert_eq!(match_regex(b"", b""), REGEXP_MATCH);
1923 }
1924
1925 #[test]
1926 fn test_literal_empty_pattern_nonempty() {
1927 assert_eq!(match_regex(b"", b"a"), REGEXP_NOMATCH);
1929 }
1930
1931 #[test]
1932 fn test_literal_single_char() {
1933 assert_eq!(match_regex(b"a", b"a"), REGEXP_MATCH);
1934 assert_eq!(match_regex(b"a", b"b"), REGEXP_NOMATCH);
1935 }
1936
1937 #[test]
1940 fn test_alternation_simple() {
1941 assert_eq!(match_regex(b"a|b", b"a"), REGEXP_MATCH);
1942 assert_eq!(match_regex(b"a|b", b"b"), REGEXP_MATCH);
1943 assert_eq!(match_regex(b"a|b", b"c"), REGEXP_NOMATCH);
1944 }
1945
1946 #[test]
1947 fn test_alternation_three() {
1948 assert_eq!(match_regex(b"a|b|c", b"a"), REGEXP_MATCH);
1949 assert_eq!(match_regex(b"a|b|c", b"b"), REGEXP_MATCH);
1950 assert_eq!(match_regex(b"a|b|c", b"c"), REGEXP_MATCH);
1951 assert_eq!(match_regex(b"a|b|c", b"d"), REGEXP_NOMATCH);
1952 }
1953
1954 #[test]
1957 fn test_zero_or_more() {
1958 assert_eq!(match_regex(b"a*", b""), REGEXP_MATCH);
1959 assert_eq!(match_regex(b"a*", b"a"), REGEXP_MATCH);
1960 assert_eq!(match_regex(b"a*", b"aaa"), REGEXP_MATCH);
1961 }
1962
1963 #[test]
1964 fn test_one_or_more() {
1965 assert_eq!(match_regex(b"a+", b"a"), REGEXP_MATCH);
1966 assert_eq!(match_regex(b"a+", b"aaa"), REGEXP_MATCH);
1967 assert_eq!(match_regex(b"a+", b""), REGEXP_NOMATCH);
1968 }
1969
1970 #[test]
1971 fn test_zero_or_one() {
1972 assert_eq!(match_regex(b"a?", b""), REGEXP_MATCH);
1973 assert_eq!(match_regex(b"a?", b"a"), REGEXP_MATCH);
1974 assert_eq!(match_regex(b"a?", b"aa"), REGEXP_NOMATCH);
1975 }
1976
1977 #[test]
1978 fn test_zero_or_more_middle() {
1979 assert_eq!(match_regex(b"a*b", b"b"), REGEXP_MATCH);
1981 assert_eq!(match_regex(b"a*b", b"ab"), REGEXP_MATCH);
1982 assert_eq!(match_regex(b"a*b", b"aaab"), REGEXP_MATCH);
1983 assert_eq!(match_regex(b"a*b", b"a"), REGEXP_NOMATCH);
1984 }
1985
1986 #[test]
1989 fn test_grouping() {
1990 assert_eq!(match_regex(b"(a)", b"a"), REGEXP_MATCH);
1991 assert_eq!(match_regex(b"(a)", b"b"), REGEXP_NOMATCH);
1992 }
1993
1994 #[test]
1995 fn test_grouping_with_quantifier() {
1996 assert_eq!(match_regex(b"(ab)+", b"ab"), REGEXP_MATCH);
1997 assert_eq!(match_regex(b"(ab)+", b"abab"), REGEXP_MATCH);
1998 assert_eq!(match_regex(b"(ab)+", b"a"), REGEXP_NOMATCH);
1999 }
2000
2001 #[test]
2004 fn test_start_anchor() {
2005 assert_eq!(match_regex(b"^a", b"a"), REGEXP_MATCH);
2006 assert_eq!(match_regex(b"^a", b"ba"), REGEXP_NOMATCH);
2007 }
2008
2009 #[test]
2010 fn test_end_anchor() {
2011 assert_eq!(match_regex(b"a$", b"a"), REGEXP_MATCH);
2012 assert_eq!(match_regex(b"a$", b"ba"), REGEXP_NOMATCH);
2013 }
2014
2015 #[test]
2016 fn test_both_anchors() {
2017 assert_eq!(match_regex(b"^a$", b"a"), REGEXP_MATCH);
2018 assert_eq!(match_regex(b"^a$", b"ab"), REGEXP_NOMATCH);
2019 assert_eq!(match_regex(b"^a$", b"ba"), REGEXP_NOMATCH);
2020 }
2021
2022 #[test]
2025 fn test_wildcard() {
2026 assert_eq!(match_regex(b".", b"a"), REGEXP_MATCH);
2027 assert_eq!(match_regex(b".", b"1"), REGEXP_MATCH);
2028 assert_eq!(match_regex(b"...", b"abc"), REGEXP_MATCH);
2029 assert_eq!(match_regex(b"...", b"ab"), REGEXP_NOMATCH);
2030 }
2031
2032 #[test]
2033 fn test_wildcard_with_literal() {
2034 assert_eq!(match_regex(b"a.b", b"axb"), REGEXP_MATCH);
2035 assert_eq!(match_regex(b"a.b", b"azb"), REGEXP_MATCH);
2036 assert_eq!(match_regex(b"a.b", b"ab"), REGEXP_NOMATCH);
2037 }
2038
2039 #[test]
2042 fn test_escaped_newline() {
2043 assert_eq!(match_regex(b"a\\nb", b"a\nb"), REGEXP_MATCH);
2044 assert_eq!(match_regex(b"a\\nb", b"ab"), REGEXP_NOMATCH);
2045 }
2046
2047 #[test]
2048 fn test_escaped_tab() {
2049 assert_eq!(match_regex(b"a\\tb", b"a\tb"), REGEXP_MATCH);
2050 }
2051
2052 #[test]
2053 fn test_escaped_metachar() {
2054 assert_eq!(match_regex(b"\\.", b"."), REGEXP_MATCH);
2056 assert_eq!(match_regex(b"\\.", b"a"), REGEXP_NOMATCH);
2057 }
2058
2059 #[test]
2062 fn test_char_class_single() {
2063 assert_eq!(match_regex(b"[a]", b"a"), REGEXP_MATCH);
2064 assert_eq!(match_regex(b"[a]", b"b"), REGEXP_NOMATCH);
2065 }
2066
2067 #[test]
2068 fn test_char_class_multiple_chars() {
2069 assert_eq!(match_regex(b"[abc]", b"a"), REGEXP_MATCH);
2070 assert_eq!(match_regex(b"[abc]", b"b"), REGEXP_MATCH);
2071 assert_eq!(match_regex(b"[abc]", b"c"), REGEXP_MATCH);
2072 assert_eq!(match_regex(b"[abc]", b"d"), REGEXP_NOMATCH);
2073 }
2074
2075 #[test]
2076 fn test_char_class_range() {
2077 assert_eq!(match_regex(b"[a-z]", b"a"), REGEXP_MATCH);
2078 assert_eq!(match_regex(b"[a-z]", b"m"), REGEXP_MATCH);
2079 assert_eq!(match_regex(b"[a-z]", b"z"), REGEXP_MATCH);
2080 assert_eq!(match_regex(b"[a-z]", b"1"), REGEXP_NOMATCH);
2081 }
2082
2083 #[test]
2084 fn test_char_class_range_with_escape() {
2085 assert_eq!(match_regex(b"[\\d]", b"5"), REGEXP_MATCH);
2086 assert_eq!(match_regex(b"[\\d]", b"a"), REGEXP_NOMATCH);
2087 }
2088
2089 #[test]
2090 fn test_multiple_char_classes() {
2091 assert_eq!(match_regex(b"[a-z0-9]+", b"abc123"), REGEXP_MATCH);
2092 assert_eq!(match_regex(b"[a-z0-9]+", b"ABC"), REGEXP_NOMATCH);
2093 }
2094
2095 #[test]
2098 fn test_digit_class() {
2099 assert_eq!(match_regex(b"\\d", b"5"), REGEXP_MATCH);
2100 assert_eq!(match_regex(b"\\d", b"a"), REGEXP_NOMATCH);
2101 }
2102
2103 #[test]
2104 fn test_word_class() {
2105 assert_eq!(match_regex(b"\\w+", b"hello"), REGEXP_MATCH);
2106 assert_eq!(match_regex(b"\\w+", b"hello123"), REGEXP_MATCH);
2107 assert_eq!(match_regex(b"\\w+", b""), REGEXP_NOMATCH);
2108 }
2109
2110 #[test]
2111 fn test_space_class() {
2112 assert_eq!(match_regex(b"\\s", b" "), REGEXP_MATCH);
2113 assert_eq!(match_regex(b"\\s", b"\t"), REGEXP_MATCH);
2114 assert_eq!(match_regex(b"\\s", b"a"), REGEXP_NOMATCH);
2115 }
2116
2117 #[test]
2120 fn test_complex_email_like() {
2121 assert_eq!(
2123 match_regex(b"\\w+@\\w+\\.\\w+", b"user@example.com"),
2124 REGEXP_MATCH
2125 );
2126 assert_eq!(match_regex(b"\\w+@\\w+\\.\\w+", b"invalid"), REGEXP_NOMATCH);
2127 }
2128
2129 #[test]
2130 fn test_complex_phone_like() {
2131 assert_eq!(
2133 match_regex(b"\\d{3}-\\d{3}-\\d{4}", b"555-123-4567"),
2134 REGEXP_MATCH
2135 );
2136 assert_eq!(
2137 match_regex(b"\\d{3}-\\d{3}-\\d{4}", b"555-123-456"),
2138 REGEXP_NOMATCH
2139 );
2140 }
2141
2142 #[test]
2143 fn test_pattern_with_all_features() {
2144 assert_eq!(match_regex(b"^(a|b)+c$", b"ac"), REGEXP_MATCH);
2146 assert_eq!(match_regex(b"^(a|b)+c$", b"bc"), REGEXP_MATCH);
2147 assert_eq!(match_regex(b"^(a|b)+c$", b"ababc"), REGEXP_MATCH);
2148 assert_eq!(match_regex(b"^(a|b)+c$", b"abd"), REGEXP_NOMATCH);
2149 }
2150
2151 #[test]
2154 fn test_exact_quantifier() {
2155 assert_eq!(match_regex(b"a{3}", b"aaa"), REGEXP_MATCH);
2156 assert_eq!(match_regex(b"a{3}", b"aa"), REGEXP_NOMATCH);
2157 assert_eq!(match_regex(b"a{3}", b"aaaa"), REGEXP_NOMATCH);
2158 }
2159
2160 #[test]
2161 fn test_between_quantifier() {
2162 assert_eq!(match_regex(b"a{2,4}", b"aa"), REGEXP_MATCH);
2163 assert_eq!(match_regex(b"a{2,4}", b"aaa"), REGEXP_MATCH);
2164 assert_eq!(match_regex(b"a{2,4}", b"aaaa"), REGEXP_MATCH);
2165 assert_eq!(match_regex(b"a{2,4}", b"a"), REGEXP_NOMATCH);
2166 assert_eq!(match_regex(b"a{2,4}", b"aaaaa"), REGEXP_NOMATCH);
2167 }
2168
2169 #[test]
2170 fn test_at_least_quantifier() {
2171 assert_eq!(match_regex(b"a{2,}", b"aa"), REGEXP_MATCH);
2172 assert_eq!(match_regex(b"a{2,}", b"aaaa"), REGEXP_MATCH);
2173 assert_eq!(match_regex(b"a{2,}", b"a"), REGEXP_NOMATCH);
2174 }
2175
2176 #[test]
2179 fn test_deterministic_literal() {
2180 let compiled = compile(b"hello");
2181 assert!(!compiled.is_null());
2182 assert_eq!(unsafe { xmlRegexpIsDeterministic(compiled) }, 1);
2183 unsafe { xmlRegFreeRegexp(compiled) };
2184 }
2185
2186 #[test]
2187 fn test_non_deterministic() {
2188 let compiled = compile(b"a|b");
2190 assert!(!compiled.is_null());
2191 assert_eq!(unsafe { xmlRegexpIsDeterministic(compiled) }, 0);
2192 unsafe { xmlRegFreeRegexp(compiled) };
2193 }
2194
2195 #[test]
2198 fn test_incremental_empty_input() {
2199 let compiled = compile(b"a*");
2200 assert!(!compiled.is_null());
2201 let ctxt = unsafe { xmlRegNewExecCtxt(compiled, ptr::null_mut()) };
2202 assert!(!ctxt.is_null());
2203 let ret = unsafe { xmlRegExecPushString(ctxt, ptr::null_mut()) };
2205 assert_eq!(ret, REGEXP_MATCH);
2207 unsafe { xmlRegFreeExecCtxt(ctxt) };
2208 unsafe { xmlRegFreeRegexp(compiled) };
2209 }
2210
2211 #[test]
2212 fn test_incremental_simple_match() {
2213 let compiled = compile(b"abc");
2214 assert!(!compiled.is_null());
2215 let ctxt = unsafe { xmlRegNewExecCtxt(compiled, ptr::null_mut()) };
2216 assert!(!ctxt.is_null());
2217 let val = to_xml_str(b"abc");
2218 let ret = unsafe { xmlRegExecPushString(ctxt, val) };
2219 assert_eq!(ret, REGEXP_MATCH);
2220 unsafe { xmlRegFreeExecCtxt(ctxt) };
2221 unsafe { xmlRegFreeRegexp(compiled) };
2222 }
2223
2224 #[test]
2227 fn test_null_pattern() {
2228 let compiled = unsafe { xmlRegexpCompile(ptr::null()) };
2229 assert!(compiled.is_null());
2230 }
2231
2232 #[test]
2233 fn test_null_input() {
2234 let compiled = compile(b"a");
2235 assert!(!compiled.is_null());
2236 let ret = unsafe { xmlRegexpExec(compiled, ptr::null()) };
2237 assert_eq!(ret, REGEXP_ERROR);
2238 unsafe { xmlRegFreeRegexp(compiled) };
2239 }
2240
2241 #[test]
2242 fn test_double_free() {
2243 let compiled = compile(b"test");
2244 assert!(!compiled.is_null());
2245 unsafe { xmlRegFreeRegexp(compiled) };
2246 }
2249
2250 #[test]
2251 fn test_print() {
2252 let compiled = compile(b"hello");
2253 assert!(!compiled.is_null());
2254 unsafe { xmlRegexpPrint(compiled) };
2255 unsafe { xmlRegFreeRegexp(compiled) };
2256 }
2257}