1 /*
2 * Copyright © 2008 Kristian Høgsberg
3 * Copyright © 2013-2019 Red Hat, Inc.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25 #include <sys/types.h>
26 #include <dev/evdev/input.h>
27
28 #include <assert.h>
29 #include <ctype.h>
30 #include <dirent.h>
31 #include <errno.h>
32 #include <limits.h>
33 #include <math.h>
34 #include <stdarg.h>
35 #include <stdbool.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <xlocale.h>
40
41 #include "util.h"
42 #include "util-evdev.h"
43 #include "util-list.h"
44
45 /* util-strings.c */
46
47 /**
48 * Return the next word in a string pointed to by state before the first
49 * separator character. Call repeatedly to tokenize a whole string.
50 *
51 * @param state Current state
52 * @param len String length of the word returned
53 * @param separators List of separator characters
54 *
55 * @return The first word in *state, NOT null-terminated
56 */
57 static const char *
next_word(const char ** state,size_t * len,const char * separators)58 next_word(const char **state, size_t *len, const char *separators)
59 {
60 const char *next = *state;
61 size_t l;
62
63 if (!*next)
64 return NULL;
65
66 next += strspn(next, separators);
67 if (!*next) {
68 *state = next;
69 return NULL;
70 }
71
72 l = strcspn(next, separators);
73 *state = next + l;
74 *len = l;
75
76 return next;
77 }
78
79 /**
80 * Return a null-terminated string array with the tokens in the input
81 * string, e.g. "one two\tthree" with a separator list of " \t" will return
82 * an array [ "one", "two", "three", NULL ] and num elements 3.
83 *
84 * Use strv_free() to free the array.
85 *
86 * Another example:
87 * result = strv_from_string("+1-2++3--4++-+5-+-", "+-", &nelem)
88 * result == [ "1", "2", "3", "4", "5", NULL ] and nelem == 5
89 *
90 * @param in Input string
91 * @param separators List of separator characters
92 * @param num_elements Number of elements found in the input string
93 *
94 * @return A null-terminated string array or NULL on errors
95 */
96 char **
strv_from_string(const char * in,const char * separators,size_t * num_elements)97 strv_from_string(const char *in, const char *separators, size_t *num_elements)
98 {
99 assert(in != NULL);
100 assert(separators != NULL);
101 assert(num_elements != NULL);
102
103 const char *s = in;
104 size_t l, nelems = 0;
105 while (next_word(&s, &l, separators) != NULL)
106 nelems++;
107
108 if (nelems == 0) {
109 *num_elements = 0;
110 return NULL;
111 }
112
113 size_t strv_len = nelems + 1; /* NULL-terminated */
114 char **strv = zalloc(strv_len * sizeof *strv);
115
116 size_t idx = 0;
117 const char *word;
118 s = in;
119 while ((word = next_word(&s, &l, separators)) != NULL) {
120 char *copy = strndup(word, l);
121 if (!copy) {
122 strv_free(strv);
123 *num_elements = 0;
124 return NULL;
125 }
126
127 strv[idx++] = copy;
128 }
129
130 *num_elements = nelems;
131
132 return strv;
133 }
134
135 /**
136 * Iterate through strv, calling func with each string and its respective index.
137 * Iteration stops successfully after max elements or at the last element,
138 * whichever occurs first.
139 *
140 * If func returns non-zero, iteration stops and strv_for_each returns
141 * that value.
142 *
143 * @return zero on success, otherwise the error returned by the callback
144 */
strv_for_each_n(const char ** strv,size_t max,strv_foreach_callback_t func,void * data)145 int strv_for_each_n(const char **strv, size_t max, strv_foreach_callback_t func, void *data)
146 {
147 for (size_t i = 0; i < max && strv && strv[i]; i++) {
148 int ret = func(strv[i], i, data);
149 if (ret)
150 return ret;
151 }
152 return 0;
153 }
154
155 /* !util-strings.c */
156
157 /* util-prop-parsers.c */
158
159 /**
160 * Parses a simple dimension string in the form of "10x40". The two
161 * numbers must be positive integers in decimal notation.
162 * On success, the two numbers are stored in w and h. On failure, w and h
163 * are unmodified.
164 *
165 * @param prop The value of the property
166 * @param w Returns the first component of the dimension
167 * @param h Returns the second component of the dimension
168 * @return true on success, false otherwise
169 */
170 bool
parse_dimension_property(const char * prop,size_t * w,size_t * h)171 parse_dimension_property(const char *prop, size_t *w, size_t *h)
172 {
173 int x, y;
174
175 if (!prop)
176 return false;
177
178 if (sscanf(prop, "%dx%d", &x, &y) != 2)
179 return false;
180
181 if (x <= 0 || y <= 0)
182 return false;
183
184 *w = (size_t)x;
185 *h = (size_t)y;
186 return true;
187 }
188
189 /**
190 * Parses a string of the format "a:b" where both a and b must be integer
191 * numbers and a > b. Also allowed is the special string value "none" which
192 * amounts to unsetting the property.
193 *
194 * @param prop The value of the property
195 * @param hi Set to the first digit or 0 in case of 'none'
196 * @param lo Set to the second digit or 0 in case of 'none'
197 * @return true on success, false otherwise
198 */
199 bool
parse_range_property(const char * prop,int * hi,int * lo)200 parse_range_property(const char *prop, int *hi, int *lo)
201 {
202 int first, second;
203
204 if (!prop)
205 return false;
206
207 if (streq(prop, "none")) {
208 *hi = 0;
209 *lo = 0;
210 return true;
211 }
212
213 if (sscanf(prop, "%d:%d", &first, &second) != 2)
214 return false;
215
216 if (second >= first)
217 return false;
218
219 *hi = first;
220 *lo = second;
221
222 return true;
223 }
224
225 bool
parse_boolean_property(const char * prop,bool * b)226 parse_boolean_property(const char *prop, bool *b)
227 {
228 if (!prop)
229 return false;
230
231 if (streq(prop, "1"))
232 *b = true;
233 else if (streq(prop, "0"))
234 *b = false;
235 else
236 return false;
237
238 return true;
239 }
240
241 static bool
parse_evcode_string(const char * s,int * type_out,int * code_out)242 parse_evcode_string(const char *s, int *type_out, int *code_out)
243 {
244 int type, code;
245
246 if (strstartswith(s, "EV_")) {
247 type = libevdev_event_type_from_name(s);
248 if (type == -1)
249 return false;
250
251 code = EVENT_CODE_UNDEFINED;
252 } else {
253 struct map {
254 const char *str;
255 int type;
256 } map[] = {
257 { "KEY_", EV_KEY },
258 { "BTN_", EV_KEY },
259 { "ABS_", EV_ABS },
260 { "REL_", EV_REL },
261 { "SW_", EV_SW },
262 };
263 bool found = false;
264
265 ARRAY_FOR_EACH(map, m) {
266 if (!strstartswith(s, m->str))
267 continue;
268
269 type = m->type;
270 code = libevdev_event_code_from_name(type, s);
271 if (code == -1)
272 return false;
273
274 found = true;
275 break;
276 }
277 if (!found)
278 return false;
279 }
280
281 *type_out = type;
282 *code_out = code;
283
284 return true;
285 }
286
287 /**
288 * Parses a string of the format "+EV_ABS;+KEY_A;-BTN_TOOL_DOUBLETAP;-ABS_X;"
289 * where each element must be + or - (enable/disable) followed by a named event
290 * type OR a named event code OR a tuple in the form of EV_KEY:0x123, i.e. a
291 * named event type followed by a hex event code.
292 *
293 * events must point to an existing array of size nevents.
294 * nevents specifies the size of the array in events and returns the number
295 * of items, elements exceeding nevents are simply ignored, just make sure
296 * events is large enough for your use-case.
297 *
298 * The results are returned as input events with type and code set, all
299 * other fields undefined. Where only the event type is specified, the code
300 * is set to EVENT_CODE_UNDEFINED.
301 *
302 * On success, events contains nevents events with each event's value set to 1
303 * or 0 depending on the + or - prefix.
304 */
305 bool
parse_evcode_property(const char * prop,struct input_event * events,size_t * nevents)306 parse_evcode_property(const char *prop, struct input_event *events, size_t *nevents)
307 {
308 bool rc = false;
309 /* A randomly chosen max so we avoid crazy quirks */
310 struct input_event evs[32];
311
312 memset(evs, 0, sizeof evs);
313
314 size_t ncodes;
315 char **strv = strv_from_string(prop, ";", &ncodes);
316 if (!strv || ncodes == 0 || ncodes > ARRAY_LENGTH(evs))
317 goto out;
318
319 ncodes = min(*nevents, ncodes);
320 for (size_t idx = 0; strv[idx]; idx++) {
321 char *s = strv[idx];
322 bool enable;
323
324 switch (*s) {
325 case '+': enable = true; break;
326 case '-': enable = false; break;
327 default:
328 goto out;
329 }
330
331 s++;
332
333 int type, code;
334
335 if (strstr(s, ":") == NULL) {
336 if (!parse_evcode_string(s, &type, &code))
337 goto out;
338 } else {
339 int consumed;
340 char stype[13] = {0}; /* EV_FF_STATUS + '\0' */
341
342 if (sscanf(s, "%12[A-Z_]:%x%n", stype, &code, &consumed) != 2 ||
343 strlen(s) != (size_t)consumed ||
344 (type = libevdev_event_type_from_name(stype)) == -1 ||
345 code < 0 || code > libevdev_event_type_get_max(type))
346 goto out;
347 }
348
349 evs[idx].type = type;
350 evs[idx].code = code;
351 evs[idx].value = enable;
352 }
353
354 memcpy(events, evs, ncodes * sizeof *events);
355 *nevents = ncodes;
356 rc = true;
357
358 out:
359 strv_free(strv);
360 return rc;
361 }
362
363 /**
364 * Parses a string of the format "+INPUT_PROP_BUTTONPAD;-INPUT_PROP_POINTER;+0x123;"
365 * where each element must be a named input prop OR a hexcode in the form
366 * 0x1234. The prefix for each element must be either '+' (enable) or '-' (disable).
367 *
368 * props must point to an existing array of size nprops.
369 * nprops specifies the size of the array in props and returns the number
370 * of elements, elements exceeding nprops are simply ignored, just make sure
371 * props is large enough for your use-case.
372 *
373 * On success, props contains nprops elements.
374 */
375 bool
parse_input_prop_property(const char * prop,struct input_prop * props_out,size_t * nprops)376 parse_input_prop_property(const char *prop, struct input_prop *props_out, size_t *nprops)
377 {
378 bool rc = false;
379 struct input_prop props[INPUT_PROP_CNT]; /* doubling up on quirks is a bug */
380
381 size_t count;
382 char **strv = strv_from_string(prop, ";", &count);
383 if (!strv || count == 0 || count > ARRAY_LENGTH(props))
384 goto out;
385
386 count = min(*nprops, count);
387 for (size_t idx = 0; strv[idx]; idx++) {
388 char *s = strv[idx];
389 unsigned int prop;
390 bool enable;
391
392 switch (*s) {
393 case '+': enable = true; break;
394 case '-': enable = false; break;
395 default:
396 goto out;
397 }
398
399 s++;
400
401 if (safe_atou_base(s, &prop, 16)) {
402 if (prop > INPUT_PROP_MAX)
403 goto out;
404 } else {
405 int val = libevdev_property_from_name(s);
406 if (val == -1)
407 goto out;
408 prop = (unsigned int)val;
409 }
410 props[idx].prop = prop;
411 props[idx].enabled = enable;
412 }
413
414 memcpy(props_out, props, count * sizeof *props);
415 *nprops = count;
416 rc = true;
417
418 out:
419 strv_free(strv);
420 return rc;
421 }
422
423 /* !util-prop-parsers.c */
424