feat(libx52io): Add _from_str and from_str_nocase APIs

This change add APIs to convert the string forms of the axis and button
names back to their corresponding enum identifiers. This is effectively
built such that a roundtrip of _to_str and _from_str will return the
same input. The _nocase variants handle case insensitive matching of the
names by folding of the ASCII alphabets A-Z and a-z only, so it doesn't
depend on localization.
pull/67/head
nirenjan 2026-04-03 18:06:30 -07:00
parent 0fdcb725af
commit c49689c1ee
5 changed files with 177 additions and 19 deletions

View File

@ -8,6 +8,7 @@
#include "config.h"
#include <stdio.h>
#include <string.h>
#include "libx52io.h"
#include "gettext.h"
@ -16,6 +17,28 @@
* on one to the enumeration definitions.
*/
/* Locale-independent: only ASCII AZ fold with az; digits and underscore unchanged */
static int x52io_ascii_strcasecmp(const char *a, const char *b)
{
for (;;) {
unsigned char ca = (unsigned char)*a++;
unsigned char cb = (unsigned char)*b++;
if (ca >= 'A' && ca <= 'Z') {
ca = (unsigned char)(ca - 'A' + 'a');
}
if (cb >= 'A' && cb <= 'Z') {
cb = (unsigned char)(cb - 'A' + 'a');
}
if (ca != cb) {
return (int)ca - (int)cb;
}
if (ca == '\0') {
return 0;
}
}
}
/* String mapping for axis */
static const char * _x52io_axis_str[LIBX52IO_AXIS_MAX] = {
[LIBX52IO_AXIS_X] = "ABS_X",
@ -92,6 +115,78 @@ const char * libx52io_button_to_str(libx52io_button button)
return NULL;
}
int libx52io_axis_from_str(const char *str, libx52io_axis *axis)
{
libx52io_axis i;
if (!str || !axis) {
return LIBX52IO_ERROR_INVALID;
}
for (i = LIBX52IO_AXIS_X; i < LIBX52IO_AXIS_MAX; i++) {
if (strcmp(str, _x52io_axis_str[i]) == 0) {
*axis = i;
return LIBX52IO_SUCCESS;
}
}
return LIBX52IO_ERROR_INVALID;
}
int libx52io_button_from_str(const char *str, libx52io_button *button)
{
libx52io_button b;
if (!str || !button) {
return LIBX52IO_ERROR_INVALID;
}
for (b = LIBX52IO_BTN_TRIGGER; b < LIBX52IO_BUTTON_MAX; b++) {
if (strcmp(str, _x52io_button_str[b]) == 0) {
*button = b;
return LIBX52IO_SUCCESS;
}
}
return LIBX52IO_ERROR_INVALID;
}
int libx52io_axis_from_str_nocase(const char *str, libx52io_axis *axis)
{
libx52io_axis i;
if (!str || !axis) {
return LIBX52IO_ERROR_INVALID;
}
for (i = LIBX52IO_AXIS_X; i < LIBX52IO_AXIS_MAX; i++) {
if (x52io_ascii_strcasecmp(str, _x52io_axis_str[i]) == 0) {
*axis = i;
return LIBX52IO_SUCCESS;
}
}
return LIBX52IO_ERROR_INVALID;
}
int libx52io_button_from_str_nocase(const char *str, libx52io_button *button)
{
libx52io_button b;
if (!str || !button) {
return LIBX52IO_ERROR_INVALID;
}
for (b = LIBX52IO_BTN_TRIGGER; b < LIBX52IO_BUTTON_MAX; b++) {
if (x52io_ascii_strcasecmp(str, _x52io_button_str[b]) == 0) {
*button = b;
return LIBX52IO_SUCCESS;
}
}
return LIBX52IO_ERROR_INVALID;
}
/* Error buffer used for building custom error strings */
static char error_buffer[256];

View File

@ -434,6 +434,62 @@ LIBX52IO_API const char *libx52io_axis_to_str(libx52io_axis axis);
*/
LIBX52IO_API const char *libx52io_button_to_str(libx52io_button button);
/**
* @brief Parse the string representation of an axis.
*
* Accepts tokens as returned by \ref libx52io_axis_to_str (e.g. \c ABS_X).
*
* @param[in] str NUL-terminated axis name
* @param[out] axis Receives the axis ID on success
*
* @returns \ref LIBX52IO_SUCCESS if \a str was recognized,
* \ref LIBX52IO_ERROR_INVALID if \a str or \a axis is NULL or \a str is unknown.
*/
LIBX52IO_API int libx52io_axis_from_str(const char *str, libx52io_axis *axis);
/**
* @brief Parse the string representation of a button.
*
* Accepts tokens as returned by \ref libx52io_button_to_str (e.g. \c BTN_FIRE).
*
* @param[in] str NUL-terminated button name
* @param[out] button Receives the button ID on success
*
* @returns \ref LIBX52IO_SUCCESS if \a str was recognized,
* \ref LIBX52IO_ERROR_INVALID if \a str or \a button is NULL or \a str is unknown.
*/
LIBX52IO_API int libx52io_button_from_str(const char *str, libx52io_button *button);
/**
* @brief Parse an axis name with ASCII case-insensitive matching.
*
* Like \ref libx52io_axis_from_str, but treats ASCII letters \c A \c Z the same
* as \c a \c z. Digits, underscore, and NUL must match exactly. Matching is
* not locale-dependent.
*
* @param[in] str NUL-terminated axis name
* @param[out] axis Receives the axis ID on success
*
* @returns \ref LIBX52IO_SUCCESS if \a str was recognized,
* \ref LIBX52IO_ERROR_INVALID if \a str or \a axis is NULL or \a str is unknown.
*/
LIBX52IO_API int libx52io_axis_from_str_nocase(const char *str, libx52io_axis *axis);
/**
* @brief Parse a button name with ASCII case-insensitive matching.
*
* Like \ref libx52io_button_from_str, but treats ASCII letters \c A \c Z the same
* as \c a \c z. Digits, underscore, and NUL must match exactly. Matching is
* not locale-dependent.
*
* @param[in] str NUL-terminated button name
* @param[out] button Receives the button ID on success
*
* @returns \ref LIBX52IO_SUCCESS if \a str was recognized,
* \ref LIBX52IO_ERROR_INVALID if \a str or \a button is NULL or \a str is unknown.
*/
LIBX52IO_API int libx52io_button_from_str_nocase(const char *str, libx52io_button *button);
/**
* @brief Get the vendor ID of the connected X52 device.
*

View File

@ -1,4 +1,4 @@
libx52io_version = '1.0.0'
libx52io_version = '1.1.0'
libx52io_files = files(
'io_core.c',
@ -37,4 +37,11 @@ test_parser = executable('test-parser', 'test_parser.c', libx52io_files,
)
test('test-parser', test_parser, protocol: 'tap')
test_io_strings = executable('test-io-strings', 'test_io_strings.c', libx52io_files,
build_by_default: false,
dependencies: [dep_cmocka, dep_hidapi, dep_intl],
include_directories: [includes],
)
test('test-io-strings', test_io_strings, protocol: 'tap')

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: libx52 0.3.3\n"
"Report-Msgid-Bugs-To: https://github.com/nirenjan/libx52/issues\n"
"POT-Creation-Date: 2026-04-01 20:47-0700\n"
"POT-Creation-Date: 2026-04-03 18:06-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,11 +17,11 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: libx52/x52_strerror.c:23 libx52io/io_strings.c:101 vkm/vkm_common.c:25
#: libx52/x52_strerror.c:23 libx52io/io_strings.c:196 vkm/vkm_common.c:25
msgid "Success"
msgstr ""
#: libx52/x52_strerror.c:24 libx52io/io_strings.c:102
#: libx52/x52_strerror.c:24 libx52io/io_strings.c:197
msgid "Initialization failure"
msgstr ""
@ -85,7 +85,7 @@ msgstr ""
msgid "System call interrupted"
msgstr ""
#: libx52/x52_strerror.c:66 libx52io/io_strings.c:125 vkm/vkm_common.c:52
#: libx52/x52_strerror.c:66 libx52io/io_strings.c:220 vkm/vkm_common.c:52
#, c-format
msgid "Unknown error %d"
msgstr ""
@ -213,23 +213,23 @@ msgstr ""
msgid "Unknown LED ID %d"
msgstr ""
#: libx52io/io_strings.c:103
#: libx52io/io_strings.c:198
msgid "No device"
msgstr ""
#: libx52io/io_strings.c:104
#: libx52io/io_strings.c:199
msgid "Invalid arguments"
msgstr ""
#: libx52io/io_strings.c:105
#: libx52io/io_strings.c:200
msgid "Connection failure"
msgstr ""
#: libx52io/io_strings.c:106
#: libx52io/io_strings.c:201
msgid "I/O error"
msgstr ""
#: libx52io/io_strings.c:107
#: libx52io/io_strings.c:202
msgid "Read timeout"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: libx52 0.2.3\n"
"Report-Msgid-Bugs-To: https://github.com/nirenjan/libx52/issues\n"
"POT-Creation-Date: 2026-04-01 20:47-0700\n"
"POT-Creation-Date: 2026-04-03 18:06-0700\n"
"PO-Revision-Date: 2026-04-01 20:50-0700\n"
"Last-Translator: Nirenjan Krishnan <nirenjan@gmail.com>\n"
"Language-Team: Dummy Language for testing i18n\n"
@ -17,11 +17,11 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.4.2\n"
#: libx52/x52_strerror.c:23 libx52io/io_strings.c:101 vkm/vkm_common.c:25
#: libx52/x52_strerror.c:23 libx52io/io_strings.c:196 vkm/vkm_common.c:25
msgid "Success"
msgstr "Uccesssay"
#: libx52/x52_strerror.c:24 libx52io/io_strings.c:102
#: libx52/x52_strerror.c:24 libx52io/io_strings.c:197
msgid "Initialization failure"
msgstr "Initializationay ailurefay"
@ -85,7 +85,7 @@ msgstr "Ipepay erroray"
msgid "System call interrupted"
msgstr "Ystemsay allcay interrupteday"
#: libx52/x52_strerror.c:66 libx52io/io_strings.c:125 vkm/vkm_common.c:52
#: libx52/x52_strerror.c:66 libx52io/io_strings.c:220 vkm/vkm_common.c:52
#, c-format
msgid "Unknown error %d"
msgstr "Unknownay erroray %d"
@ -213,23 +213,23 @@ msgstr "Ottlethray"
msgid "Unknown LED ID %d"
msgstr "Unknownay EDLay IDay %d"
#: libx52io/io_strings.c:103
#: libx52io/io_strings.c:198
msgid "No device"
msgstr "Onay eviceday"
#: libx52io/io_strings.c:104
#: libx52io/io_strings.c:199
msgid "Invalid arguments"
msgstr "Invaliday argumentsay"
#: libx52io/io_strings.c:105
#: libx52io/io_strings.c:200
msgid "Connection failure"
msgstr "Onnectioncay ailurefay"
#: libx52io/io_strings.c:106
#: libx52io/io_strings.c:201
msgid "I/O error"
msgstr "I/O erroray"
#: libx52io/io_strings.c:107
#: libx52io/io_strings.c:202
msgid "Read timeout"
msgstr "Eadray imeouttay"