1package kittygraphics
2
3import (
4 "fmt"
5)
6
7// ParseControls parses the comma-separated control header of an APC.
8func ParseControls(header []byte) (Controls, error) { return parseControls(header) }
9
10// ParseHeader is an alias for ParseControls.
11func ParseHeader(header []byte) (Controls, error) { return ParseControls(header) }
12
13func parseControls(header []byte) (Controls, error) {
14 var c Controls
15 if len(header) == 0 {
16 return c, nil
17 }
18 var seen [256]bool
19 start := 0
20 for i := 0; i <= len(header); i++ {
21 if i != len(header) && header[i] != ',' {
22 continue
23 }
24 field := header[start:i]
25 if len(field) < 3 || field[1] != '=' {
26 return Controls{}, fmt.Errorf("%w: control %q", ErrInvalidCommand, field)
27 }
28 key := field[0]
29 if seen[key] {
30 return Controls{}, fmt.Errorf("%w: %c", ErrDuplicateControl, key)
31 }
32 seen[key] = true
33 value := field[2:]
34 if err := parseControl(&c, key, value); err != nil {
35 return Controls{}, err
36 }
37 start = i + 1
38 }
39 if c.HasAction && !c.Action.Valid() {
40 return Controls{}, fmt.Errorf("%w: %q", ErrUnknownAction, c.Action)
41 }
42 if c.HasImageID && c.HasImageNumber {
43 return Controls{}, fmt.Errorf("%w: image id and image number are mutually exclusive", ErrInvalidCommand)
44 }
45 if c.HasQuiet && c.Quiet > QuietAll {
46 return Controls{}, fmt.Errorf("%w: quiet=%d", ErrInvalidCommand, c.Quiet)
47 }
48 if c.HasMore && c.More > 1 {
49 return Controls{}, fmt.Errorf("%w: m=%d", ErrInvalidCommand, c.More)
50 }
51 if c.HasCursor && c.Cursor > 1 {
52 return Controls{}, fmt.Errorf("%w: C=%d", ErrInvalidCommand, c.Cursor)
53 }
54 return c, nil
55}
56
57func parseControl(c *Controls, key byte, value []byte) error {
58 if len(value) == 0 {
59 return fmt.Errorf("%w: empty %c value", ErrInvalidInteger, key)
60 }
61 switch ControlKey(key) {
62 case ControlAction:
63 if len(value) != 1 {
64 return fmt.Errorf("%w: action %q", ErrUnknownAction, value)
65 }
66 c.Action, c.HasAction = Action(value[0]), true
67 case ControlCompression:
68 n, err := parseUint(value)
69 if err != nil {
70 return fieldError(key, err)
71 }
72 c.Compression, c.HasCompression = n, true
73 case ControlFormat:
74 n, err := parseUint(value)
75 if err != nil {
76 return fieldError(key, err)
77 }
78 f := Format(n)
79 if !f.Valid() {
80 return fmt.Errorf("%w: f=%d", ErrInvalidCommand, n)
81 }
82 c.Format, c.HasFormat = f, true
83 case ControlImageID:
84 return setID(&c.ImageID, &c.HasImageID, key, value)
85 case ControlImageNumber:
86 return setID(&c.ImageNumber, &c.HasImageNumber, key, value)
87 case ControlMore:
88 return setUint(&c.More, &c.HasMore, key, value)
89 case ControlQuiet:
90 n, err := parseUint(value)
91 if err != nil {
92 return fieldError(key, err)
93 }
94 if n > uint64(QuietAll) {
95 return fmt.Errorf("%w: quiet=%d", ErrInvalidCommand, n)
96 }
97 c.Quiet, c.HasQuiet = Quiet(n), true
98 case ControlWidth:
99 return setUint(&c.Width, &c.HasWidth, key, value)
100 case ControlHeight:
101 return setUint(&c.Height, &c.HasHeight, key, value)
102 case ControlColumns:
103 return setUint(&c.Columns, &c.HasColumns, key, value)
104 case ControlRows:
105 return setUint(&c.Rows, &c.HasRows, key, value)
106 case ControlX:
107 return setUint(&c.X, &c.HasX, key, value)
108 case ControlY:
109 return setUint(&c.Y, &c.HasY, key, value)
110 case ControlSourceWidth:
111 return setUint(&c.SourceWidth, &c.HasSourceWidth, key, value)
112 case ControlSourceHeight:
113 return setUint(&c.SourceHeight, &c.HasSourceHeight, key, value)
114 case ControlLayer:
115 return setInt(&c.Layer, &c.HasLayer, key, value)
116 case ControlPlacementID:
117 return setID(&c.PlacementID, &c.HasPlacementID, key, value)
118 case ControlDelete:
119 if len(value) != 1 {
120 return fmt.Errorf("%w: d=%q", ErrInvalidCommand, value)
121 }
122 d := DeleteTarget(value[0])
123 switch d {
124 case DeleteImage, DeleteImageNumber, DeleteCell, DeleteAll, DeleteAllImages, DeleteCellAll:
125 c.Delete, c.HasDelete = d, true
126 default:
127 return fmt.Errorf("%w: d=%q", ErrInvalidCommand, value)
128 }
129 case ControlTransmission:
130 if len(value) != 1 || Transmission(value[0]) != TransmissionDirect {
131 return fmt.Errorf("%w: t=%q", ErrUnsupported, value)
132 }
133 c.Transmission, c.HasTransmission = TransmissionDirect, true
134 case ControlCursor:
135 return setUint(&c.Cursor, &c.HasCursor, key, value)
136 case ControlSourceX:
137 return setUint(&c.SourceX, &c.HasSourceX, key, value)
138 case ControlSourceY:
139 return setUint(&c.SourceY, &c.HasSourceY, key, value)
140 case ControlCellOffsetX:
141 return setInt(&c.CellOffsetX, &c.HasCellOffsetX, key, value)
142 case ControlCellOffsetY:
143 return setInt(&c.CellOffsetY, &c.HasCellOffsetY, key, value)
144 case ControlParent:
145 return setUint(&c.Parent, &c.HasParent, key, value)
146 default:
147 // Kitty reserves unknown controls for forward-compatible extensions.
148 // Commands that depend on an unsupported control are rejected later by
149 // their action-specific validation.
150 return nil
151 }
152 return nil
153}
154
155func fieldError(key byte, err error) error {
156 return fmt.Errorf("%w: %c: %w", ErrInvalidCommand, key, err)
157}
158
159func setID(dst *uint64, present *bool, key byte, value []byte) error {
160 if err := setUint(dst, present, key, value); err != nil {
161 return err
162 }
163 if *dst > uint64(^uint32(0)) {
164 return fieldError(key, ErrIntegerOverflow)
165 }
166 return nil
167}
168
169func setUint(dst *uint64, present *bool, key byte, value []byte) error {
170 n, err := parseUint(value)
171 if err != nil {
172 return fieldError(key, err)
173 }
174 if n > MaxKittyID {
175 return fieldError(key, ErrIntegerOverflow)
176 }
177 *dst = n
178 if present != nil {
179 *present = true
180 }
181 return nil
182}
183
184func setInt(dst *int64, present *bool, key byte, value []byte) error {
185 n, err := parseInt(value)
186 if err != nil {
187 return fieldError(key, err)
188 }
189 if n < -1<<31 || n > 1<<31-1 {
190 return fieldError(key, ErrIntegerOverflow)
191 }
192 *dst = n
193 if present != nil {
194 *present = true
195 }
196 return nil
197}
198
199func parseUint(value []byte) (uint64, error) {
200 if len(value) == 0 {
201 return 0, ErrInvalidInteger
202 }
203 var n uint64
204 for _, b := range value {
205 if b < '0' || b > '9' {
206 return 0, ErrInvalidInteger
207 }
208 digit := uint64(b - '0')
209 if n > (^uint64(0)-digit)/10 {
210 return 0, ErrIntegerOverflow
211 }
212 n = n*10 + digit
213 }
214 return n, nil
215}
216
217func parseInt(value []byte) (int64, error) {
218 if len(value) == 0 {
219 return 0, ErrInvalidInteger
220 }
221 negative := value[0] == '-'
222 if negative {
223 value = value[1:]
224 if len(value) == 0 {
225 return 0, ErrInvalidInteger
226 }
227 }
228 n, err := parseUint(value)
229 if err != nil {
230 return 0, err
231 }
232 if negative {
233 if n > uint64(1)<<63 {
234 return 0, ErrIntegerOverflow
235 }
236 if n == uint64(1)<<63 {
237 return -1 << 63, nil
238 }
239 return -int64(n), nil
240 }
241 if n > uint64(^uint64(0)>>1) {
242 return 0, ErrIntegerOverflow
243 }
244 return int64(n), nil
245}