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