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  }