Unified · Split
| @@ -94,9 +94,10 @@ fn parse_long_keywords(input: &[u8], p: &mut Pattern, cursor: &mut usize) -> Res · 93 unmodified lines | |||
| 94 | return Ok(()); | 94 | return Ok(()); |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | for keyword in split_on_non_escaped_char(input, b',') { | 97 | split_on_non_escaped_char(input, b',', |keyword| { |
| 98 | let attr_prefix = b"attr:"; | ||
| 98 | match keyword { | 99 | match keyword { |
| 99 | b"attr" => continue, | 100 | b"attr" => {} |
| 100 | b"top" => p.signature |= MagicSignature::TOP, | 101 | b"top" => p.signature |= MagicSignature::TOP, |
| 101 | b"icase" => p.signature |= MagicSignature::ICASE, | 102 | b"icase" => p.signature |= MagicSignature::ICASE, |
| 102 | b"exclude" => p.signature |= MagicSignature::EXCLUDE, | 103 | b"exclude" => p.signature |= MagicSignature::EXCLUDE, |
| @@ -108,9 +109,9 @@ fn parse_long_keywords(input: &[u8], p: &mut Pattern, cursor: &mut usize) -> Res · 5 unmodified lines | |||
| 108 | MatchMode::Literal => return Err(Error::IncompatibleSearchModes), | 109 | MatchMode::Literal => return Err(Error::IncompatibleSearchModes), |
| 109 | _ => p.search_mode = MatchMode::PathAwareGlob, | 110 | _ => p.search_mode = MatchMode::PathAwareGlob, |
| 110 | }, | 111 | }, |
| 111 | _ if keyword.starts_with(b"attr:") => { | 112 | _ if keyword.starts_with(attr_prefix) => { |
| 112 | if p.attributes.is_empty() { | 113 | if p.attributes.is_empty() { |
| 113 | p.attributes = parse_attributes(&keyword[5..])?; | 114 | p.attributes = parse_attributes(&keyword[attr_prefix.len()..])?; |
| 114 | } else { | 115 | } else { |
| 115 | return Err(Error::MultipleAttributeSpecifications); | 116 | return Err(Error::MultipleAttributeSpecifications); |
| 116 | } | 117 | } |
| @@ -120,25 +121,28 @@ fn parse_long_keywords(input: &[u8], p: &mut Pattern, cursor: &mut usize) -> Res · 3 unmodified lines | |||
| 120 | keyword: BString::from(keyword), | 121 | keyword: BString::from(keyword), |
| 121 | }); | 122 | }); |
| 122 | } | 123 | } |
| 123 | } | 124 | }; |
| 124 | } | 125 | Ok(()) |
| 125 | 126 | }) | |
| 126 | Ok(()) | ||
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | fn split_on_non_escaped_char(input: &[u8], split_char: u8) -> Vec<&[u8]> { | 129 | fn split_on_non_escaped_char( |
| 130 | let mut keywords = Vec::new(); | 130 | input: &[u8], |
| 131 | split_char: u8, | ||
| 132 | mut f: impl FnMut(&[u8]) -> Result<(), Error>, | ||
| 133 | ) -> Result<(), Error> { | ||
| 131 | let mut i = 0; | 134 | let mut i = 0; |
| 132 | let mut last = 0; | 135 | let mut last = 0; |
| 133 | for window in input.windows(2) { | 136 | for window in input.windows(2) { |
| 134 | i += 1; | 137 | i += 1; |
| 135 | if window[0] != b'\\' && window[1] == split_char { | 138 | if window[0] != b'\\' && window[1] == split_char { |
| 136 | keywords.push(&input[last..i]); | 139 | let keyword = &input[last..i]; |
| 140 | f(keyword)?; | ||
| 137 | last = i + 1; | 141 | last = i + 1; |
| 138 | } | 142 | } |
| 139 | } | 143 | } |
| 140 | keywords.push(&input[last..]); | 144 | let last_keyword = &input[last..]; |
| 141 | keywords | 145 | f(last_keyword) |
| 142 | } | 146 | } |
| 143 | 147 | ||
| 144 | fn parse_attributes(input: &[u8]) -> Result<Vec<git_attributes::Assignment>, Error> { | 148 | fn parse_attributes(input: &[u8]) -> Result<Vec<git_attributes::Assignment>, Error> { |
| @@ -175,7 +179,7 @@ fn unescape_attribute_values(input: &BStr) -> Result<Cow<'_, BStr>, Error> { · 30 unmodified lines | |||
| 175 | match out { | 179 | match out { |
| 176 | Cow::Borrowed(_) => { | 180 | Cow::Borrowed(_) => { |
| 177 | let end = out.len() + attr.len() + 1; | 181 | let end = out.len() + attr.len() + 1; |
| 178 | out = Cow::Borrowed(&input[0..end.min(input.len())]) | 182 | out = Cow::Borrowed(&input[0..end.min(input.len())]); |
| 179 | } | 183 | } |
| 180 | Cow::Owned(_) => { | 184 | Cow::Owned(_) => { |
| 181 | let out = out.to_mut(); | 185 | let out = out.to_mut(); |