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 }