mod.rs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. //! fnmatch implementation
  2. use alloc::borrow::Cow;
  3. use alloc::vec::Vec;
  4. use core::slice;
  5. use platform::types::*;
  6. use posix_regex::PosixRegex;
  7. use posix_regex::compile::{Collation, Token, Range};
  8. const ONCE: Range = Range(1, Some(1));
  9. pub const FNM_NOMATCH: c_int = 1;
  10. pub const FNM_NOESCAPE: c_int = 1;
  11. pub const FNM_PATHNAME: c_int = 2;
  12. pub const FNM_PERIOD: c_int = 4;
  13. pub const FNM_CASEFOLD: c_int = 8;
  14. // TODO: FNM_EXTMATCH
  15. unsafe fn tokenize(mut pattern: *const u8, flags: c_int) -> Vec<(Token, Range)> {
  16. fn any(leading: bool, flags: c_int) -> Token {
  17. let mut list = Vec::new();
  18. if flags & FNM_PATHNAME == FNM_PATHNAME {
  19. list.push(Collation::Char(b'/'))
  20. }
  21. if leading && flags & FNM_PERIOD == FNM_PERIOD {
  22. list.push(Collation::Char(b'.'))
  23. }
  24. Token::OneOf { invert: true, list }
  25. }
  26. fn can_push(leading: bool, flags: c_int, c: u8) -> bool {
  27. (c != b'/' || flags & FNM_PATHNAME != FNM_PATHNAME)
  28. && (c != b'.' || !leading || flags & FNM_PERIOD != FNM_PERIOD)
  29. }
  30. fn is_leading(flags: c_int, c: u8) -> bool {
  31. c == b'/' && flags & FNM_PATHNAME == FNM_PATHNAME
  32. }
  33. let mut tokens = Vec::new();
  34. let mut leading = true;
  35. while *pattern != 0 {
  36. let was_leading = leading;
  37. leading = false;
  38. let c = *pattern;
  39. pattern = pattern.offset(1);
  40. tokens.push(match c {
  41. b'\\' if flags & FNM_NOESCAPE == FNM_NOESCAPE => {
  42. let c = *pattern;
  43. if c == 0 {
  44. // Trailing backslash. Maybe error here?
  45. break;
  46. }
  47. pattern = pattern.offset(1);
  48. leading = is_leading(flags, c);
  49. (Token::Char(c), ONCE)
  50. },
  51. b'?' => (any(was_leading, flags), ONCE),
  52. b'*' => (any(was_leading, flags), Range(0, None)),
  53. b'[' => {
  54. let mut list: Vec<Collation> = Vec::new();
  55. let invert = if *pattern == b'!' {
  56. pattern = pattern.offset(1);
  57. true
  58. } else {
  59. false
  60. };
  61. loop {
  62. let mut c = *pattern;
  63. if c == 0 {
  64. break;
  65. }
  66. pattern = pattern.offset(1);
  67. match c {
  68. b']' => break,
  69. b'\\' => {
  70. c = *pattern;
  71. pattern = pattern.offset(1);
  72. if c == 0 {
  73. // Trailing backslash. Maybe error?
  74. break;
  75. }
  76. }
  77. _ => (),
  78. }
  79. if *pattern == b'-' && *pattern.offset(1) != 0 {
  80. let end = *pattern.offset(1);
  81. pattern = pattern.offset(2);
  82. for c in c..=end {
  83. if can_push(was_leading, flags, c) {
  84. list.push(Collation::Char(c));
  85. }
  86. }
  87. } else {
  88. if can_push(was_leading, flags, c) {
  89. list.push(Collation::Char(c));
  90. }
  91. }
  92. }
  93. // Otherwise, there was no closing ]. Maybe error?
  94. (Token::OneOf {
  95. invert,
  96. list
  97. }, ONCE)
  98. }
  99. c => {
  100. leading = is_leading(flags, c);
  101. (Token::Char(c), ONCE)
  102. }
  103. })
  104. }
  105. tokens
  106. }
  107. #[no_mangle]
  108. pub unsafe extern "C" fn fnmatch(pattern: *const c_char, input: *const c_char, flags: c_int) -> c_int {
  109. let mut len = 0;
  110. while *input.offset(len) != 0 {
  111. len += 1;
  112. }
  113. let input = slice::from_raw_parts(input as *const u8, len as usize);
  114. let mut tokens = tokenize(pattern as *const u8, flags);
  115. tokens.push((Token::End, ONCE));
  116. if PosixRegex::new(Cow::Owned(vec![tokens]))
  117. .case_insensitive(flags & FNM_CASEFOLD == FNM_CASEFOLD)
  118. .matches_exact(input).is_some() {
  119. 0
  120. } else {
  121. FNM_NOMATCH
  122. }
  123. }