github.com/containerd/Containerd@v1.4.13/filters/scanner.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package filters 18 19 import ( 20 "unicode" 21 "unicode/utf8" 22 ) 23 24 const ( 25 tokenEOF = -(iota + 1) 26 tokenQuoted 27 tokenValue 28 tokenField 29 tokenSeparator 30 tokenOperator 31 tokenIllegal 32 ) 33 34 type token rune 35 36 func (t token) String() string { 37 switch t { 38 case tokenEOF: 39 return "EOF" 40 case tokenQuoted: 41 return "Quoted" 42 case tokenValue: 43 return "Value" 44 case tokenField: 45 return "Field" 46 case tokenSeparator: 47 return "Separator" 48 case tokenOperator: 49 return "Operator" 50 case tokenIllegal: 51 return "Illegal" 52 } 53 54 return string(t) 55 } 56 57 func (t token) GoString() string { 58 return "token" + t.String() 59 } 60 61 type scanner struct { 62 input string 63 pos int 64 ppos int // bounds the current rune in the string 65 value bool 66 err string 67 } 68 69 func (s *scanner) init(input string) { 70 s.input = input 71 s.pos = 0 72 s.ppos = 0 73 } 74 75 func (s *scanner) next() rune { 76 if s.pos >= len(s.input) { 77 return tokenEOF 78 } 79 s.pos = s.ppos 80 81 r, w := utf8.DecodeRuneInString(s.input[s.ppos:]) 82 s.ppos += w 83 if r == utf8.RuneError { 84 if w > 0 { 85 s.error("rune error") 86 return tokenIllegal 87 } 88 return tokenEOF 89 } 90 91 if r == 0 { 92 s.error("unexpected null") 93 return tokenIllegal 94 } 95 96 return r 97 } 98 99 func (s *scanner) peek() rune { 100 pos := s.pos 101 ppos := s.ppos 102 ch := s.next() 103 s.pos = pos 104 s.ppos = ppos 105 return ch 106 } 107 108 func (s *scanner) scan() (nextp int, tk token, text string) { 109 var ( 110 ch = s.next() 111 pos = s.pos 112 ) 113 114 chomp: 115 switch { 116 case ch == tokenEOF: 117 case ch == tokenIllegal: 118 case isQuoteRune(ch): 119 if !s.scanQuoted(ch) { 120 return pos, tokenIllegal, s.input[pos:s.ppos] 121 } 122 return pos, tokenQuoted, s.input[pos:s.ppos] 123 case isSeparatorRune(ch): 124 s.value = false 125 return pos, tokenSeparator, s.input[pos:s.ppos] 126 case isOperatorRune(ch): 127 s.scanOperator() 128 s.value = true 129 return pos, tokenOperator, s.input[pos:s.ppos] 130 case unicode.IsSpace(ch): 131 // chomp 132 ch = s.next() 133 pos = s.pos 134 goto chomp 135 case s.value: 136 s.scanValue() 137 s.value = false 138 return pos, tokenValue, s.input[pos:s.ppos] 139 case isFieldRune(ch): 140 s.scanField() 141 return pos, tokenField, s.input[pos:s.ppos] 142 } 143 144 return s.pos, token(ch), "" 145 } 146 147 func (s *scanner) scanField() { 148 for { 149 ch := s.peek() 150 if !isFieldRune(ch) { 151 break 152 } 153 s.next() 154 } 155 } 156 157 func (s *scanner) scanOperator() { 158 for { 159 ch := s.peek() 160 switch ch { 161 case '=', '!', '~': 162 s.next() 163 default: 164 return 165 } 166 } 167 } 168 169 func (s *scanner) scanValue() { 170 for { 171 ch := s.peek() 172 if !isValueRune(ch) { 173 break 174 } 175 s.next() 176 } 177 } 178 179 func (s *scanner) scanQuoted(quote rune) bool { 180 var illegal bool 181 ch := s.next() // read character after quote 182 for ch != quote { 183 if ch == '\n' || ch < 0 { 184 s.error("quoted literal not terminated") 185 return false 186 } 187 if ch == '\\' { 188 var legal bool 189 ch, legal = s.scanEscape(quote) 190 if !legal { 191 illegal = true 192 } 193 } else { 194 ch = s.next() 195 } 196 } 197 return !illegal 198 } 199 200 func (s *scanner) scanEscape(quote rune) (ch rune, legal bool) { 201 ch = s.next() // read character after '/' 202 switch ch { 203 case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote: 204 // nothing to do 205 ch = s.next() 206 legal = true 207 case '0', '1', '2', '3', '4', '5', '6', '7': 208 ch, legal = s.scanDigits(ch, 8, 3) 209 case 'x': 210 ch, legal = s.scanDigits(s.next(), 16, 2) 211 case 'u': 212 ch, legal = s.scanDigits(s.next(), 16, 4) 213 case 'U': 214 ch, legal = s.scanDigits(s.next(), 16, 8) 215 default: 216 s.error("illegal escape sequence") 217 } 218 return 219 } 220 221 func (s *scanner) scanDigits(ch rune, base, n int) (rune, bool) { 222 for n > 0 && digitVal(ch) < base { 223 ch = s.next() 224 n-- 225 } 226 if n > 0 { 227 s.error("illegal numeric escape sequence") 228 return ch, false 229 } 230 return ch, true 231 } 232 233 func (s *scanner) error(msg string) { 234 if s.err == "" { 235 s.err = msg 236 } 237 } 238 239 func digitVal(ch rune) int { 240 switch { 241 case '0' <= ch && ch <= '9': 242 return int(ch - '0') 243 case 'a' <= ch && ch <= 'f': 244 return int(ch - 'a' + 10) 245 case 'A' <= ch && ch <= 'F': 246 return int(ch - 'A' + 10) 247 } 248 return 16 // larger than any legal digit val 249 } 250 251 func isFieldRune(r rune) bool { 252 return (r == '_' || isAlphaRune(r) || isDigitRune(r)) 253 } 254 255 func isAlphaRune(r rune) bool { 256 return r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' 257 } 258 259 func isDigitRune(r rune) bool { 260 return r >= '0' && r <= '9' 261 } 262 263 func isOperatorRune(r rune) bool { 264 switch r { 265 case '=', '!', '~': 266 return true 267 } 268 269 return false 270 } 271 272 func isQuoteRune(r rune) bool { 273 switch r { 274 case '/', '|', '"': // maybe add single quoting? 275 return true 276 } 277 278 return false 279 } 280 281 func isSeparatorRune(r rune) bool { 282 switch r { 283 case ',', '.': 284 return true 285 } 286 287 return false 288 } 289 290 func isValueRune(r rune) bool { 291 return r != ',' && !unicode.IsSpace(r) && 292 (unicode.IsLetter(r) || 293 unicode.IsDigit(r) || 294 unicode.IsNumber(r) || 295 unicode.IsGraphic(r) || 296 unicode.IsPunct(r)) 297 }