xref: /freebsd/usr.sbin/moused/moused/util.c (revision aef807876c305587c60f73e2cd914115d22a53fd)
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