1a46c0ec8Sopenharmony_ci/*
2a46c0ec8Sopenharmony_ci * Copyright © 2013-2019 Red Hat, Inc.
3a46c0ec8Sopenharmony_ci *
4a46c0ec8Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
5a46c0ec8Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
6a46c0ec8Sopenharmony_ci * to deal in the Software without restriction, including without limitation
7a46c0ec8Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8a46c0ec8Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
9a46c0ec8Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
10a46c0ec8Sopenharmony_ci *
11a46c0ec8Sopenharmony_ci * The above copyright notice and this permission notice (including the next
12a46c0ec8Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
13a46c0ec8Sopenharmony_ci * Software.
14a46c0ec8Sopenharmony_ci *
15a46c0ec8Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16a46c0ec8Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17a46c0ec8Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18a46c0ec8Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19a46c0ec8Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20a46c0ec8Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21a46c0ec8Sopenharmony_ci * DEALINGS IN THE SOFTWARE.
22a46c0ec8Sopenharmony_ci */
23a46c0ec8Sopenharmony_ci
24a46c0ec8Sopenharmony_ci#include "util-prop-parsers.h"
25a46c0ec8Sopenharmony_ci
26a46c0ec8Sopenharmony_ci#include <libevdev/libevdev.h>
27a46c0ec8Sopenharmony_ci#include <string.h>
28a46c0ec8Sopenharmony_ci
29a46c0ec8Sopenharmony_ci#include "util-macros.h"
30a46c0ec8Sopenharmony_ci#include "util-strings.h"
31a46c0ec8Sopenharmony_ci
32a46c0ec8Sopenharmony_ci/* Helper function to parse the mouse DPI tag from udev.
33a46c0ec8Sopenharmony_ci * The tag is of the form:
34a46c0ec8Sopenharmony_ci * MOUSE_DPI=400 *1000 2000
35a46c0ec8Sopenharmony_ci * or
36a46c0ec8Sopenharmony_ci * MOUSE_DPI=400@125 *1000@125 2000@125
37a46c0ec8Sopenharmony_ci * Where the * indicates the default value and @number indicates device poll
38a46c0ec8Sopenharmony_ci * rate.
39a46c0ec8Sopenharmony_ci * Numbers should be in ascending order, and if rates are present they should
40a46c0ec8Sopenharmony_ci * be present for all entries.
41a46c0ec8Sopenharmony_ci *
42a46c0ec8Sopenharmony_ci * When parsing the mouse DPI property, if we find an error we just return 0
43a46c0ec8Sopenharmony_ci * since it's obviously invalid, the caller will treat that as an error and
44a46c0ec8Sopenharmony_ci * use a reasonable default instead. If the property contains multiple DPI
45a46c0ec8Sopenharmony_ci * settings but none flagged as default, we return the last because we're
46a46c0ec8Sopenharmony_ci * lazy and that's a silly way to set the property anyway.
47a46c0ec8Sopenharmony_ci *
48a46c0ec8Sopenharmony_ci * @param prop The value of the udev property (without the MOUSE_DPI=)
49a46c0ec8Sopenharmony_ci * @return The default dpi value on success, 0 on error
50a46c0ec8Sopenharmony_ci */
51a46c0ec8Sopenharmony_ciint
52a46c0ec8Sopenharmony_ciparse_mouse_dpi_property(const char *prop)
53a46c0ec8Sopenharmony_ci{
54a46c0ec8Sopenharmony_ci	bool is_default = false;
55a46c0ec8Sopenharmony_ci	int nread, dpi = 0, rate;
56a46c0ec8Sopenharmony_ci
57a46c0ec8Sopenharmony_ci	if (!prop)
58a46c0ec8Sopenharmony_ci		return 0;
59a46c0ec8Sopenharmony_ci
60a46c0ec8Sopenharmony_ci	while (*prop != 0) {
61a46c0ec8Sopenharmony_ci		if (*prop == ' ') {
62a46c0ec8Sopenharmony_ci			prop++;
63a46c0ec8Sopenharmony_ci			continue;
64a46c0ec8Sopenharmony_ci		}
65a46c0ec8Sopenharmony_ci		if (*prop == '*') {
66a46c0ec8Sopenharmony_ci			prop++;
67a46c0ec8Sopenharmony_ci			is_default = true;
68a46c0ec8Sopenharmony_ci			if (!isdigit(prop[0]))
69a46c0ec8Sopenharmony_ci				return 0;
70a46c0ec8Sopenharmony_ci		}
71a46c0ec8Sopenharmony_ci
72a46c0ec8Sopenharmony_ci		/* While we don't do anything with the rate right now we
73a46c0ec8Sopenharmony_ci		 * will validate that, if it's present, it is non-zero and
74a46c0ec8Sopenharmony_ci		 * positive
75a46c0ec8Sopenharmony_ci		 */
76a46c0ec8Sopenharmony_ci		rate = 1;
77a46c0ec8Sopenharmony_ci		nread = 0;
78a46c0ec8Sopenharmony_ci		sscanf(prop, "%d@%d%n", &dpi, &rate, &nread);
79a46c0ec8Sopenharmony_ci		if (!nread)
80a46c0ec8Sopenharmony_ci			sscanf(prop, "%d%n", &dpi, &nread);
81a46c0ec8Sopenharmony_ci		if (!nread || dpi <= 0 || rate <= 0 || prop[nread] == '@')
82a46c0ec8Sopenharmony_ci			return 0;
83a46c0ec8Sopenharmony_ci
84a46c0ec8Sopenharmony_ci		if (is_default)
85a46c0ec8Sopenharmony_ci			break;
86a46c0ec8Sopenharmony_ci		prop += nread;
87a46c0ec8Sopenharmony_ci	}
88a46c0ec8Sopenharmony_ci	return dpi;
89a46c0ec8Sopenharmony_ci}
90a46c0ec8Sopenharmony_ci
91a46c0ec8Sopenharmony_ci/**
92a46c0ec8Sopenharmony_ci * Helper function to parse the MOUSE_WHEEL_CLICK_COUNT property from udev.
93a46c0ec8Sopenharmony_ci * Property is of the form:
94a46c0ec8Sopenharmony_ci * MOUSE_WHEEL_CLICK_COUNT=<integer>
95a46c0ec8Sopenharmony_ci * Where the number indicates the number of wheel clicks per 360 deg
96a46c0ec8Sopenharmony_ci * rotation.
97a46c0ec8Sopenharmony_ci *
98a46c0ec8Sopenharmony_ci * @param prop The value of the udev property (without the MOUSE_WHEEL_CLICK_COUNT=)
99a46c0ec8Sopenharmony_ci * @return The click count of the wheel (may be negative) or 0 on error.
100a46c0ec8Sopenharmony_ci */
101a46c0ec8Sopenharmony_ciint
102a46c0ec8Sopenharmony_ciparse_mouse_wheel_click_count_property(const char *prop)
103a46c0ec8Sopenharmony_ci{
104a46c0ec8Sopenharmony_ci	int count = 0;
105a46c0ec8Sopenharmony_ci
106a46c0ec8Sopenharmony_ci	if (!prop)
107a46c0ec8Sopenharmony_ci		return 0;
108a46c0ec8Sopenharmony_ci
109a46c0ec8Sopenharmony_ci	if (!safe_atoi(prop, &count) || abs(count) > 360)
110a46c0ec8Sopenharmony_ci		return 0;
111a46c0ec8Sopenharmony_ci
112a46c0ec8Sopenharmony_ci        return count;
113a46c0ec8Sopenharmony_ci}
114a46c0ec8Sopenharmony_ci
115a46c0ec8Sopenharmony_ci/**
116a46c0ec8Sopenharmony_ci *
117a46c0ec8Sopenharmony_ci * Helper function to parse the MOUSE_WHEEL_CLICK_ANGLE property from udev.
118a46c0ec8Sopenharmony_ci * Property is of the form:
119a46c0ec8Sopenharmony_ci * MOUSE_WHEEL_CLICK_ANGLE=<integer>
120a46c0ec8Sopenharmony_ci * Where the number indicates the degrees travelled for each click.
121a46c0ec8Sopenharmony_ci *
122a46c0ec8Sopenharmony_ci * @param prop The value of the udev property (without the MOUSE_WHEEL_CLICK_ANGLE=)
123a46c0ec8Sopenharmony_ci * @return The angle of the wheel (may be negative) or 0 on error.
124a46c0ec8Sopenharmony_ci */
125a46c0ec8Sopenharmony_ciint
126a46c0ec8Sopenharmony_ciparse_mouse_wheel_click_angle_property(const char *prop)
127a46c0ec8Sopenharmony_ci{
128a46c0ec8Sopenharmony_ci	int angle = 0;
129a46c0ec8Sopenharmony_ci
130a46c0ec8Sopenharmony_ci	if (!prop)
131a46c0ec8Sopenharmony_ci		return 0;
132a46c0ec8Sopenharmony_ci
133a46c0ec8Sopenharmony_ci	if (!safe_atoi(prop, &angle) || abs(angle) > 360)
134a46c0ec8Sopenharmony_ci		return 0;
135a46c0ec8Sopenharmony_ci
136a46c0ec8Sopenharmony_ci        return angle;
137a46c0ec8Sopenharmony_ci}
138a46c0ec8Sopenharmony_ci
139a46c0ec8Sopenharmony_ci/**
140a46c0ec8Sopenharmony_ci * Parses a simple dimension string in the form of "10x40". The two
141a46c0ec8Sopenharmony_ci * numbers must be positive integers in decimal notation.
142a46c0ec8Sopenharmony_ci * On success, the two numbers are stored in w and h. On failure, w and h
143a46c0ec8Sopenharmony_ci * are unmodified.
144a46c0ec8Sopenharmony_ci *
145a46c0ec8Sopenharmony_ci * @param prop The value of the property
146a46c0ec8Sopenharmony_ci * @param w Returns the first component of the dimension
147a46c0ec8Sopenharmony_ci * @param h Returns the second component of the dimension
148a46c0ec8Sopenharmony_ci * @return true on success, false otherwise
149a46c0ec8Sopenharmony_ci */
150a46c0ec8Sopenharmony_cibool
151a46c0ec8Sopenharmony_ciparse_dimension_property(const char *prop, size_t *w, size_t *h)
152a46c0ec8Sopenharmony_ci{
153a46c0ec8Sopenharmony_ci	int x, y;
154a46c0ec8Sopenharmony_ci
155a46c0ec8Sopenharmony_ci	if (!prop)
156a46c0ec8Sopenharmony_ci		return false;
157a46c0ec8Sopenharmony_ci
158a46c0ec8Sopenharmony_ci	if (sscanf(prop, "%dx%d", &x, &y) != 2)
159a46c0ec8Sopenharmony_ci		return false;
160a46c0ec8Sopenharmony_ci
161a46c0ec8Sopenharmony_ci	if (x <= 0 || y <= 0)
162a46c0ec8Sopenharmony_ci		return false;
163a46c0ec8Sopenharmony_ci
164a46c0ec8Sopenharmony_ci	*w = (size_t)x;
165a46c0ec8Sopenharmony_ci	*h = (size_t)y;
166a46c0ec8Sopenharmony_ci	return true;
167a46c0ec8Sopenharmony_ci}
168a46c0ec8Sopenharmony_ci
169a46c0ec8Sopenharmony_ci/**
170a46c0ec8Sopenharmony_ci * Parses a set of 6 space-separated floats.
171a46c0ec8Sopenharmony_ci *
172a46c0ec8Sopenharmony_ci * @param prop The string value of the property
173a46c0ec8Sopenharmony_ci * @param calibration Returns the six components
174a46c0ec8Sopenharmony_ci * @return true on success, false otherwise
175a46c0ec8Sopenharmony_ci */
176a46c0ec8Sopenharmony_cibool
177a46c0ec8Sopenharmony_ciparse_calibration_property(const char *prop, float calibration_out[6])
178a46c0ec8Sopenharmony_ci{
179a46c0ec8Sopenharmony_ci	if (!prop)
180a46c0ec8Sopenharmony_ci		return false;
181a46c0ec8Sopenharmony_ci
182a46c0ec8Sopenharmony_ci	bool rc = false;
183a46c0ec8Sopenharmony_ci
184a46c0ec8Sopenharmony_ci	size_t num_calibration;
185a46c0ec8Sopenharmony_ci	char **strv = strv_from_string(prop, " ", &num_calibration);
186a46c0ec8Sopenharmony_ci	if (!strv || num_calibration < 6)
187a46c0ec8Sopenharmony_ci		goto out;
188a46c0ec8Sopenharmony_ci
189a46c0ec8Sopenharmony_ci	float calibration[6];
190a46c0ec8Sopenharmony_ci	for (size_t idx = 0; idx < 6; idx++) {
191a46c0ec8Sopenharmony_ci		double v;
192a46c0ec8Sopenharmony_ci		if (!safe_atod(strv[idx], &v))
193a46c0ec8Sopenharmony_ci			goto out;
194a46c0ec8Sopenharmony_ci
195a46c0ec8Sopenharmony_ci		calibration[idx] = v;
196a46c0ec8Sopenharmony_ci	}
197a46c0ec8Sopenharmony_ci
198a46c0ec8Sopenharmony_ci	memcpy(calibration_out, calibration, sizeof(calibration));
199a46c0ec8Sopenharmony_ci	rc = true;
200a46c0ec8Sopenharmony_ci
201a46c0ec8Sopenharmony_ciout:
202a46c0ec8Sopenharmony_ci	strv_free(strv);
203a46c0ec8Sopenharmony_ci	return rc;
204a46c0ec8Sopenharmony_ci}
205a46c0ec8Sopenharmony_ci
206a46c0ec8Sopenharmony_cibool
207a46c0ec8Sopenharmony_ciparse_switch_reliability_property(const char *prop,
208a46c0ec8Sopenharmony_ci				  enum switch_reliability *reliability)
209a46c0ec8Sopenharmony_ci{
210a46c0ec8Sopenharmony_ci	if (!prop) {
211a46c0ec8Sopenharmony_ci		*reliability = RELIABILITY_RELIABLE;
212a46c0ec8Sopenharmony_ci		return true;
213a46c0ec8Sopenharmony_ci	}
214a46c0ec8Sopenharmony_ci
215a46c0ec8Sopenharmony_ci	if (streq(prop, "reliable"))
216a46c0ec8Sopenharmony_ci		*reliability = RELIABILITY_RELIABLE;
217a46c0ec8Sopenharmony_ci	else if (streq(prop, "unreliable"))
218a46c0ec8Sopenharmony_ci		*reliability = RELIABILITY_UNRELIABLE;
219a46c0ec8Sopenharmony_ci	else if (streq(prop, "write_open"))
220a46c0ec8Sopenharmony_ci		*reliability = RELIABILITY_WRITE_OPEN;
221a46c0ec8Sopenharmony_ci	else
222a46c0ec8Sopenharmony_ci		return false;
223a46c0ec8Sopenharmony_ci
224a46c0ec8Sopenharmony_ci	return true;
225a46c0ec8Sopenharmony_ci}
226a46c0ec8Sopenharmony_ci
227a46c0ec8Sopenharmony_ci/**
228a46c0ec8Sopenharmony_ci * Parses a string with the allowed values: "below"
229a46c0ec8Sopenharmony_ci * The value refers to the position of the touchpad (relative to the
230a46c0ec8Sopenharmony_ci * keyboard, i.e. your average laptop would be 'below')
231a46c0ec8Sopenharmony_ci *
232a46c0ec8Sopenharmony_ci * @param prop The value of the property
233a46c0ec8Sopenharmony_ci * @param layout The layout
234a46c0ec8Sopenharmony_ci * @return true on success, false otherwise
235a46c0ec8Sopenharmony_ci */
236a46c0ec8Sopenharmony_cibool
237a46c0ec8Sopenharmony_ciparse_tpkbcombo_layout_poperty(const char *prop,
238a46c0ec8Sopenharmony_ci			       enum tpkbcombo_layout *layout)
239a46c0ec8Sopenharmony_ci{
240a46c0ec8Sopenharmony_ci	if (!prop)
241a46c0ec8Sopenharmony_ci		return false;
242a46c0ec8Sopenharmony_ci
243a46c0ec8Sopenharmony_ci	if (streq(prop, "below")) {
244a46c0ec8Sopenharmony_ci		*layout = TPKBCOMBO_LAYOUT_BELOW;
245a46c0ec8Sopenharmony_ci		return true;
246a46c0ec8Sopenharmony_ci	}
247a46c0ec8Sopenharmony_ci
248a46c0ec8Sopenharmony_ci	return false;
249a46c0ec8Sopenharmony_ci}
250a46c0ec8Sopenharmony_ci
251a46c0ec8Sopenharmony_ci/**
252a46c0ec8Sopenharmony_ci * Parses a string of the format "a:b" where both a and b must be integer
253a46c0ec8Sopenharmony_ci * numbers and a > b. Also allowed is the special string value "none" which
254a46c0ec8Sopenharmony_ci * amounts to unsetting the property.
255a46c0ec8Sopenharmony_ci *
256a46c0ec8Sopenharmony_ci * @param prop The value of the property
257a46c0ec8Sopenharmony_ci * @param hi Set to the first digit or 0 in case of 'none'
258a46c0ec8Sopenharmony_ci * @param lo Set to the second digit or 0 in case of 'none'
259a46c0ec8Sopenharmony_ci * @return true on success, false otherwise
260a46c0ec8Sopenharmony_ci */
261a46c0ec8Sopenharmony_cibool
262a46c0ec8Sopenharmony_ciparse_range_property(const char *prop, int *hi, int *lo)
263a46c0ec8Sopenharmony_ci{
264a46c0ec8Sopenharmony_ci	int first, second;
265a46c0ec8Sopenharmony_ci
266a46c0ec8Sopenharmony_ci	if (!prop)
267a46c0ec8Sopenharmony_ci		return false;
268a46c0ec8Sopenharmony_ci
269a46c0ec8Sopenharmony_ci	if (streq(prop, "none")) {
270a46c0ec8Sopenharmony_ci		*hi = 0;
271a46c0ec8Sopenharmony_ci		*lo = 0;
272a46c0ec8Sopenharmony_ci		return true;
273a46c0ec8Sopenharmony_ci	}
274a46c0ec8Sopenharmony_ci
275a46c0ec8Sopenharmony_ci	if (sscanf(prop, "%d:%d", &first, &second) != 2)
276a46c0ec8Sopenharmony_ci		return false;
277a46c0ec8Sopenharmony_ci
278a46c0ec8Sopenharmony_ci	if (second >= first)
279a46c0ec8Sopenharmony_ci		return false;
280a46c0ec8Sopenharmony_ci
281a46c0ec8Sopenharmony_ci	*hi = first;
282a46c0ec8Sopenharmony_ci	*lo = second;
283a46c0ec8Sopenharmony_ci
284a46c0ec8Sopenharmony_ci	return true;
285a46c0ec8Sopenharmony_ci}
286a46c0ec8Sopenharmony_ci
287a46c0ec8Sopenharmony_cibool
288a46c0ec8Sopenharmony_ciparse_boolean_property(const char *prop, bool *b)
289a46c0ec8Sopenharmony_ci{
290a46c0ec8Sopenharmony_ci	if (!prop)
291a46c0ec8Sopenharmony_ci		return false;
292a46c0ec8Sopenharmony_ci
293a46c0ec8Sopenharmony_ci	if (streq(prop, "1"))
294a46c0ec8Sopenharmony_ci		*b = true;
295a46c0ec8Sopenharmony_ci	else if (streq(prop, "0"))
296a46c0ec8Sopenharmony_ci		*b = false;
297a46c0ec8Sopenharmony_ci	else
298a46c0ec8Sopenharmony_ci		return false;
299a46c0ec8Sopenharmony_ci
300a46c0ec8Sopenharmony_ci	return true;
301a46c0ec8Sopenharmony_ci}
302a46c0ec8Sopenharmony_ci
303a46c0ec8Sopenharmony_cistatic bool
304a46c0ec8Sopenharmony_ciparse_evcode_string(const char *s, int *type_out, int *code_out)
305a46c0ec8Sopenharmony_ci{
306a46c0ec8Sopenharmony_ci	int type, code;
307a46c0ec8Sopenharmony_ci
308a46c0ec8Sopenharmony_ci	if (strneq(s, "EV_", 3)) {
309a46c0ec8Sopenharmony_ci		type = libevdev_event_type_from_name(s);
310a46c0ec8Sopenharmony_ci		if (type == -1)
311a46c0ec8Sopenharmony_ci			return false;
312a46c0ec8Sopenharmony_ci
313a46c0ec8Sopenharmony_ci		code = EVENT_CODE_UNDEFINED;
314a46c0ec8Sopenharmony_ci	} else {
315a46c0ec8Sopenharmony_ci		struct map {
316a46c0ec8Sopenharmony_ci			const char *str;
317a46c0ec8Sopenharmony_ci			int type;
318a46c0ec8Sopenharmony_ci		} map[] = {
319a46c0ec8Sopenharmony_ci			{ "KEY_", EV_KEY },
320a46c0ec8Sopenharmony_ci			{ "BTN_", EV_KEY },
321a46c0ec8Sopenharmony_ci			{ "ABS_", EV_ABS },
322a46c0ec8Sopenharmony_ci			{ "REL_", EV_REL },
323a46c0ec8Sopenharmony_ci			{ "SW_", EV_SW },
324a46c0ec8Sopenharmony_ci		};
325a46c0ec8Sopenharmony_ci		bool found = false;
326a46c0ec8Sopenharmony_ci
327a46c0ec8Sopenharmony_ci		ARRAY_FOR_EACH(map, m) {
328a46c0ec8Sopenharmony_ci			if (!strstartswith(s, m->str))
329a46c0ec8Sopenharmony_ci				continue;
330a46c0ec8Sopenharmony_ci
331a46c0ec8Sopenharmony_ci			type = m->type;
332a46c0ec8Sopenharmony_ci			code = libevdev_event_code_from_name(type, s);
333a46c0ec8Sopenharmony_ci			if (code == -1)
334a46c0ec8Sopenharmony_ci				return false;
335a46c0ec8Sopenharmony_ci
336a46c0ec8Sopenharmony_ci			found = true;
337a46c0ec8Sopenharmony_ci			break;
338a46c0ec8Sopenharmony_ci		}
339a46c0ec8Sopenharmony_ci		if (!found)
340a46c0ec8Sopenharmony_ci			return false;
341a46c0ec8Sopenharmony_ci	}
342a46c0ec8Sopenharmony_ci
343a46c0ec8Sopenharmony_ci	*type_out = type;
344a46c0ec8Sopenharmony_ci	*code_out = code;
345a46c0ec8Sopenharmony_ci
346a46c0ec8Sopenharmony_ci	return true;
347a46c0ec8Sopenharmony_ci}
348a46c0ec8Sopenharmony_ci
349a46c0ec8Sopenharmony_ci/**
350a46c0ec8Sopenharmony_ci * Parses a string of the format "+EV_ABS;+KEY_A;-BTN_TOOL_DOUBLETAP;-ABS_X;"
351a46c0ec8Sopenharmony_ci * where each element must be + or - (enable/disable) followed by a named event
352a46c0ec8Sopenharmony_ci * type OR a named event code OR a tuple in the form of EV_KEY:0x123, i.e. a
353a46c0ec8Sopenharmony_ci * named event type followed by a hex event code.
354a46c0ec8Sopenharmony_ci *
355a46c0ec8Sopenharmony_ci * events must point to an existing array of size nevents.
356a46c0ec8Sopenharmony_ci * nevents specifies the size of the array in events and returns the number
357a46c0ec8Sopenharmony_ci * of items, elements exceeding nevents are simply ignored, just make sure
358a46c0ec8Sopenharmony_ci * events is large enough for your use-case.
359a46c0ec8Sopenharmony_ci *
360a46c0ec8Sopenharmony_ci * The results are returned as input events with type and code set, all
361a46c0ec8Sopenharmony_ci * other fields undefined. Where only the event type is specified, the code
362a46c0ec8Sopenharmony_ci * is set to EVENT_CODE_UNDEFINED.
363a46c0ec8Sopenharmony_ci *
364a46c0ec8Sopenharmony_ci * On success, events contains nevents events with each event's value set to 1
365a46c0ec8Sopenharmony_ci * or 0 depending on the + or - prefix.
366a46c0ec8Sopenharmony_ci */
367a46c0ec8Sopenharmony_cibool
368a46c0ec8Sopenharmony_ciparse_evcode_property(const char *prop, struct input_event *events, size_t *nevents)
369a46c0ec8Sopenharmony_ci{
370a46c0ec8Sopenharmony_ci	bool rc = false;
371a46c0ec8Sopenharmony_ci	/* A randomly chosen max so we avoid crazy quirks */
372a46c0ec8Sopenharmony_ci	struct input_event evs[32];
373a46c0ec8Sopenharmony_ci
374a46c0ec8Sopenharmony_ci	memset(evs, 0, sizeof evs);
375a46c0ec8Sopenharmony_ci
376a46c0ec8Sopenharmony_ci	size_t ncodes;
377a46c0ec8Sopenharmony_ci	char **strv = strv_from_string(prop, ";", &ncodes);
378a46c0ec8Sopenharmony_ci	if (!strv || ncodes == 0 || ncodes > ARRAY_LENGTH(evs))
379a46c0ec8Sopenharmony_ci		goto out;
380a46c0ec8Sopenharmony_ci
381a46c0ec8Sopenharmony_ci	ncodes = min(*nevents, ncodes);
382a46c0ec8Sopenharmony_ci	for (size_t idx = 0; strv[idx]; idx++) {
383a46c0ec8Sopenharmony_ci		char *s = strv[idx];
384a46c0ec8Sopenharmony_ci		bool enable;
385a46c0ec8Sopenharmony_ci
386a46c0ec8Sopenharmony_ci		switch (*s) {
387a46c0ec8Sopenharmony_ci		case '+': enable = true; break;
388a46c0ec8Sopenharmony_ci		case '-': enable = false; break;
389a46c0ec8Sopenharmony_ci		default:
390a46c0ec8Sopenharmony_ci			goto out;
391a46c0ec8Sopenharmony_ci		}
392a46c0ec8Sopenharmony_ci
393a46c0ec8Sopenharmony_ci		s++;
394a46c0ec8Sopenharmony_ci
395a46c0ec8Sopenharmony_ci		int type, code;
396a46c0ec8Sopenharmony_ci
397a46c0ec8Sopenharmony_ci		if (strstr(s, ":") == NULL) {
398a46c0ec8Sopenharmony_ci			if (!parse_evcode_string(s, &type, &code))
399a46c0ec8Sopenharmony_ci				goto out;
400a46c0ec8Sopenharmony_ci		} else {
401a46c0ec8Sopenharmony_ci			int consumed;
402a46c0ec8Sopenharmony_ci			char stype[13] = {0}; /* EV_FF_STATUS + '\0' */
403a46c0ec8Sopenharmony_ci
404a46c0ec8Sopenharmony_ci			if (sscanf(s, "%12[A-Z_]:%x%n", stype, &code, &consumed) != 2 ||
405a46c0ec8Sopenharmony_ci			    strlen(s) != (size_t)consumed ||
406a46c0ec8Sopenharmony_ci			    (type = libevdev_event_type_from_name(stype)) == -1 ||
407a46c0ec8Sopenharmony_ci			    code < 0 || code > libevdev_event_type_get_max(type))
408a46c0ec8Sopenharmony_ci			    goto out;
409a46c0ec8Sopenharmony_ci		}
410a46c0ec8Sopenharmony_ci
411a46c0ec8Sopenharmony_ci		evs[idx].type = type;
412a46c0ec8Sopenharmony_ci		evs[idx].code = code;
413a46c0ec8Sopenharmony_ci		evs[idx].value = enable;
414a46c0ec8Sopenharmony_ci	}
415a46c0ec8Sopenharmony_ci
416a46c0ec8Sopenharmony_ci	memcpy(events, evs, ncodes * sizeof *events);
417a46c0ec8Sopenharmony_ci	*nevents = ncodes;
418a46c0ec8Sopenharmony_ci	rc = true;
419a46c0ec8Sopenharmony_ci
420a46c0ec8Sopenharmony_ciout:
421a46c0ec8Sopenharmony_ci	strv_free(strv);
422a46c0ec8Sopenharmony_ci	return rc;
423a46c0ec8Sopenharmony_ci}
424a46c0ec8Sopenharmony_ci
425a46c0ec8Sopenharmony_ci/**
426a46c0ec8Sopenharmony_ci * Parses a string of the format "+INPUT_PROP_BUTTONPAD;-INPUT_PROP_POINTER;+0x123;"
427a46c0ec8Sopenharmony_ci * where each element must be a named input prop OR a hexcode in the form
428a46c0ec8Sopenharmony_ci * 0x1234. The prefix for each element must be either '+' (enable) or '-' (disable).
429a46c0ec8Sopenharmony_ci *
430a46c0ec8Sopenharmony_ci * props must point to an existing array of size nprops.
431a46c0ec8Sopenharmony_ci * nprops specifies the size of the array in props and returns the number
432a46c0ec8Sopenharmony_ci * of elements, elements exceeding nprops are simply ignored, just make sure
433a46c0ec8Sopenharmony_ci * props is large enough for your use-case.
434a46c0ec8Sopenharmony_ci *
435a46c0ec8Sopenharmony_ci * On success, props contains nprops elements.
436a46c0ec8Sopenharmony_ci */
437a46c0ec8Sopenharmony_cibool
438a46c0ec8Sopenharmony_ciparse_input_prop_property(const char *prop, struct input_prop *props_out, size_t *nprops)
439a46c0ec8Sopenharmony_ci{
440a46c0ec8Sopenharmony_ci	bool rc = false;
441a46c0ec8Sopenharmony_ci	struct input_prop props[INPUT_PROP_CNT]; /* doubling up on quirks is a bug */
442a46c0ec8Sopenharmony_ci
443a46c0ec8Sopenharmony_ci	size_t count;
444a46c0ec8Sopenharmony_ci	char **strv = strv_from_string(prop, ";", &count);
445a46c0ec8Sopenharmony_ci	if (!strv || count == 0 || count > ARRAY_LENGTH(props))
446a46c0ec8Sopenharmony_ci		goto out;
447a46c0ec8Sopenharmony_ci
448a46c0ec8Sopenharmony_ci	count = min(*nprops, count);
449a46c0ec8Sopenharmony_ci	for (size_t idx = 0; strv[idx]; idx++) {
450a46c0ec8Sopenharmony_ci		char *s = strv[idx];
451a46c0ec8Sopenharmony_ci		unsigned int prop;
452a46c0ec8Sopenharmony_ci		bool enable;
453a46c0ec8Sopenharmony_ci
454a46c0ec8Sopenharmony_ci		switch (*s) {
455a46c0ec8Sopenharmony_ci		case '+': enable = true; break;
456a46c0ec8Sopenharmony_ci		case '-': enable = false; break;
457a46c0ec8Sopenharmony_ci		default:
458a46c0ec8Sopenharmony_ci			goto out;
459a46c0ec8Sopenharmony_ci		}
460a46c0ec8Sopenharmony_ci
461a46c0ec8Sopenharmony_ci		s++;
462a46c0ec8Sopenharmony_ci
463a46c0ec8Sopenharmony_ci		if (safe_atou_base(s, &prop, 16)) {
464a46c0ec8Sopenharmony_ci			if (prop > INPUT_PROP_MAX)
465a46c0ec8Sopenharmony_ci				goto out;
466a46c0ec8Sopenharmony_ci		} else {
467a46c0ec8Sopenharmony_ci			int val = libevdev_property_from_name(s);
468a46c0ec8Sopenharmony_ci			if (val == -1)
469a46c0ec8Sopenharmony_ci				goto out;
470a46c0ec8Sopenharmony_ci			prop = (unsigned int)val;
471a46c0ec8Sopenharmony_ci		}
472a46c0ec8Sopenharmony_ci		props[idx].prop = prop;
473a46c0ec8Sopenharmony_ci		props[idx].enabled = enable;
474a46c0ec8Sopenharmony_ci	}
475a46c0ec8Sopenharmony_ci
476a46c0ec8Sopenharmony_ci	memcpy(props_out, props, count * sizeof *props);
477a46c0ec8Sopenharmony_ci	*nprops = count;
478a46c0ec8Sopenharmony_ci	rc = true;
479a46c0ec8Sopenharmony_ci
480a46c0ec8Sopenharmony_ciout:
481a46c0ec8Sopenharmony_ci	strv_free(strv);
482a46c0ec8Sopenharmony_ci	return rc;
483a46c0ec8Sopenharmony_ci}
484a46c0ec8Sopenharmony_ci
485a46c0ec8Sopenharmony_ci/**
486a46c0ec8Sopenharmony_ci * Parse the property value for the EVDEV_ABS_00 properties. Spec is
487a46c0ec8Sopenharmony_ci *  EVDEV_ABS_00=min:max:res:fuzz:flat
488a46c0ec8Sopenharmony_ci * where any element may be empty and subsequent elements may not be
489a46c0ec8Sopenharmony_ci * present. So we have to parse
490a46c0ec8Sopenharmony_ci *  EVDEV_ABS_00=min:max:res
491a46c0ec8Sopenharmony_ci *  EVDEV_ABS_00=::res
492a46c0ec8Sopenharmony_ci *  EVDEV_ABS_00=::res:fuzz:
493a46c0ec8Sopenharmony_ci *
494a46c0ec8Sopenharmony_ci * Returns a mask of the bits set and the absinfo struct with the values.
495a46c0ec8Sopenharmony_ci * The abs value for an unset bit is undefined.
496a46c0ec8Sopenharmony_ci */
497a46c0ec8Sopenharmony_ciuint32_t
498a46c0ec8Sopenharmony_ciparse_evdev_abs_prop(const char *prop, struct input_absinfo *abs)
499a46c0ec8Sopenharmony_ci{
500a46c0ec8Sopenharmony_ci	char *str = safe_strdup(prop);
501a46c0ec8Sopenharmony_ci	char *current, *next;
502a46c0ec8Sopenharmony_ci	uint32_t mask = 0;
503a46c0ec8Sopenharmony_ci	int bit = ABS_MASK_MIN;
504a46c0ec8Sopenharmony_ci	int *val;
505a46c0ec8Sopenharmony_ci	int values[5];
506a46c0ec8Sopenharmony_ci
507a46c0ec8Sopenharmony_ci	/* basic sanity check: 5 digits for min/max, 3 for resolution, fuzz,
508a46c0ec8Sopenharmony_ci	 * flat and the colons. That's plenty, anything over is garbage */
509a46c0ec8Sopenharmony_ci	if (!prop || strlen(prop) > 24)
510a46c0ec8Sopenharmony_ci		goto out;
511a46c0ec8Sopenharmony_ci
512a46c0ec8Sopenharmony_ci	current = str;
513a46c0ec8Sopenharmony_ci	val = values;
514a46c0ec8Sopenharmony_ci	while (current && *current != '\0' && bit <= ABS_MASK_FLAT) {
515a46c0ec8Sopenharmony_ci		if (*current != ':') {
516a46c0ec8Sopenharmony_ci			int v;
517a46c0ec8Sopenharmony_ci			next = index(current, ':');
518a46c0ec8Sopenharmony_ci			if (next)
519a46c0ec8Sopenharmony_ci				*next = '\0';
520a46c0ec8Sopenharmony_ci
521a46c0ec8Sopenharmony_ci			if (!safe_atoi(current, &v)) {
522a46c0ec8Sopenharmony_ci				mask = 0;
523a46c0ec8Sopenharmony_ci				goto out;
524a46c0ec8Sopenharmony_ci			}
525a46c0ec8Sopenharmony_ci			*val = v;
526a46c0ec8Sopenharmony_ci			mask |= bit;
527a46c0ec8Sopenharmony_ci			current = next ? ++next : NULL;
528a46c0ec8Sopenharmony_ci		} else {
529a46c0ec8Sopenharmony_ci			current++;
530a46c0ec8Sopenharmony_ci		}
531a46c0ec8Sopenharmony_ci		bit <<= 1;
532a46c0ec8Sopenharmony_ci		val++;
533a46c0ec8Sopenharmony_ci	}
534a46c0ec8Sopenharmony_ci
535a46c0ec8Sopenharmony_ci	if (mask & ABS_MASK_MIN)
536a46c0ec8Sopenharmony_ci		abs->minimum = values[0];
537a46c0ec8Sopenharmony_ci	if (mask & ABS_MASK_MAX)
538a46c0ec8Sopenharmony_ci		abs->maximum = values[1];
539a46c0ec8Sopenharmony_ci	if (mask & ABS_MASK_RES)
540a46c0ec8Sopenharmony_ci		abs->resolution = values[2];
541a46c0ec8Sopenharmony_ci	if (mask & ABS_MASK_FUZZ)
542a46c0ec8Sopenharmony_ci		abs->fuzz = values[3];
543a46c0ec8Sopenharmony_ci	if (mask & ABS_MASK_FLAT)
544a46c0ec8Sopenharmony_ci		abs->flat = values[4];
545a46c0ec8Sopenharmony_ci
546a46c0ec8Sopenharmony_ciout:
547a46c0ec8Sopenharmony_ci	free(str);
548a46c0ec8Sopenharmony_ci
549a46c0ec8Sopenharmony_ci	return mask;
550a46c0ec8Sopenharmony_ci}
551