github.com/quay/claircore@v1.5.28/rhel/dockerfile/unquote.go (about)

     1  package dockerfile
     2  
     3  import (
     4  	"unicode/utf8"
     5  
     6  	"golang.org/x/text/transform"
     7  )
     8  
     9  // Unquote is a text transformer that undoes one level of quoting.
    10  //
    11  // It does not enforce that the entire text passed to the Transform method is a
    12  // valid quoted string; leading or trailing characters or multiple consecutive
    13  // quoted strings are fine.
    14  //
    15  // Any unrecognized escape pairs are passed through unchanged. Multicharacter
    16  // escape sequences like "\xNN" or "\NNN" or "\uNNNN" are unsupported.
    17  type Unquote struct {
    18  	state unquoteState
    19  	esc   bool
    20  
    21  	escchar rune
    22  }
    23  
    24  // NewUnquote returns an Unquote ready to use with the escape metacharacter set
    25  // to '\'.
    26  func NewUnquote() *Unquote {
    27  	return &Unquote{
    28  		escchar: '\\',
    29  	}
    30  }
    31  
    32  // Escape changes the escape metacharacter.
    33  //
    34  // This is possible to do at any time, but may be inadvisable.
    35  func (u *Unquote) Escape(r rune) {
    36  	u.escchar = r
    37  }
    38  
    39  // Assert that this is a Transformer.
    40  var _ transform.Transformer = (*Unquote)(nil)
    41  
    42  // Reset implements transform.Transformer.
    43  func (u *Unquote) Reset() {
    44  	u.esc = false
    45  	u.state = unquoteBare
    46  }
    47  
    48  // Transform implements transform.Transformer.
    49  func (u *Unquote) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
    50  	r, sz := rune(0), 0
    51  	for ; nSrc < len(src); nSrc += sz {
    52  		r, sz = utf8.DecodeRune(src[nSrc:])
    53  		if r == utf8.RuneError {
    54  			err = transform.ErrShortSrc
    55  			return
    56  		}
    57  		if len(dst) == nDst {
    58  			err = transform.ErrShortDst
    59  			return
    60  		}
    61  		switch u.state {
    62  		case unquoteBare:
    63  			switch {
    64  			case !u.esc && r == u.escchar:
    65  				u.esc = true
    66  				continue
    67  			case !u.esc && r == '"':
    68  				u.state = unquoteDQuote
    69  				continue
    70  			case !u.esc && r == '\'':
    71  				u.state = unquoteSQuote
    72  				continue
    73  			case u.esc && r == '"':
    74  				u.esc = false
    75  			case u.esc && r == '\'':
    76  				u.esc = false
    77  			case u.esc && r == u.escchar:
    78  				u.esc = false
    79  			case u.esc:
    80  				u.esc = false
    81  				// Add in the omitted escape rune.
    82  				nDst += utf8.EncodeRune(dst[nDst:], u.escchar)
    83  			}
    84  		case unquoteSQuote:
    85  			switch {
    86  			case !u.esc && r == u.escchar:
    87  				u.esc = true
    88  				continue
    89  			case !u.esc && r == '\'':
    90  				u.state = unquoteBare
    91  				continue
    92  			case u.esc && r == '\'':
    93  				u.esc = false
    94  			case u.esc && r == u.escchar:
    95  				u.esc = false
    96  			case u.esc:
    97  				u.esc = false
    98  				// Add in the omitted escape rune.
    99  				nDst += utf8.EncodeRune(dst[nDst:], u.escchar)
   100  			}
   101  		case unquoteDQuote:
   102  			switch {
   103  			case !u.esc && r == u.escchar:
   104  				u.esc = true
   105  				continue
   106  			case !u.esc && r == '"':
   107  				u.state = unquoteBare
   108  				continue
   109  			case u.esc && r == '"':
   110  				u.esc = false
   111  			case u.esc && r == u.escchar:
   112  				u.esc = false
   113  			case u.esc:
   114  				u.esc = false
   115  				if r, ok := escTable[r]; ok {
   116  					nDst += utf8.EncodeRune(dst[nDst:], r)
   117  					continue
   118  				}
   119  				nDst += utf8.EncodeRune(dst[nDst:], u.escchar)
   120  			}
   121  		default:
   122  			panic("state botch")
   123  		}
   124  		nDst += utf8.EncodeRune(dst[nDst:], r)
   125  	}
   126  	return nDst, nSrc, nil
   127  }
   128  
   129  // UnquoteState tracks the current state of the transformer.
   130  type unquoteState uint8
   131  
   132  const (
   133  	// In a bare string: both quotes can be escaped, along with the
   134  	// metacharacter.
   135  	unquoteBare unquoteState = iota
   136  	// In a single-quoted string: single quote and the metacharacter can be
   137  	// escaped.
   138  	unquoteSQuote
   139  	// In a double-quoted string: double quote, the metacharacter, and the usual
   140  	// suspects of C-style escapes are escaped.
   141  	unquoteDQuote
   142  )
   143  
   144  // EscTable is a mapping of common single-letter escapes to their "real"
   145  // encodings.
   146  //
   147  // Explicitly-encoded characters (e.g. \000, \x00, \u0000) are not handled here.
   148  // '?' is omitted because it's trigraph braindamage and an explicitly encoded
   149  // character.
   150  //
   151  // See also ascii(7).
   152  var escTable = map[rune]rune{
   153  	'0': 0x00,
   154  	'a': 0x07,
   155  	'b': 0x08,
   156  	't': 0x09,
   157  	'n': 0x0a,
   158  	'v': 0x0b,
   159  	'f': 0x0c,
   160  	'r': 0x0d,
   161  	// 'e' is omitted because it's almost always used to construct other escape
   162  	// sequences for terminals and the like.
   163  }