mirror of https://github.com/nirenjan/libx52.git
feat: Generate identifiers for communication
In order to improve security around the command processing, I plan to upgrade the communication protocol to use a fixed structure with an optional payload. As a first pass on this, I added a script to generate the IDs automatically from the configuration, as well as a script to map the module names and log levels to integer IDs. This also replaces the manual lmap_* and array_get_index lookup tables, so I don't have to always deal with creating an entry in constants.h and command.c and making sure they are in sync.lipc-refactor
parent
772017661d
commit
5fecdd3929
|
|
@ -208,40 +208,9 @@ static void cmd_config(char *buffer, int *buflen, int argc, char **argv)
|
|||
ERR_fmt("Unknown subcommand '%s' for 'config' command", argv[1]);
|
||||
}
|
||||
|
||||
struct level_map {
|
||||
int level;
|
||||
const char *string;
|
||||
};
|
||||
|
||||
static int lmap_get_level(const struct level_map *map, const char *string, int notfound)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; map[i].string != NULL; i++) {
|
||||
if (strcasecmp(map[i].string, string) == 0) {
|
||||
return map[i].level;
|
||||
}
|
||||
}
|
||||
|
||||
return notfound;
|
||||
}
|
||||
|
||||
static const char *lmap_get_string(const struct level_map *map, int level)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; map[i].string != NULL; i++) {
|
||||
if (map[i].level == level) {
|
||||
return map[i].string;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define DATA_LMAP(map, level, resp) do {\
|
||||
#define DATA_LMAP(level, resp) do {\
|
||||
int input_level_ ## __LINE__ = level; \
|
||||
const char *lmap_level_ ## __LINE__ = lmap_get_string(map, input_level_ ## __LINE__); \
|
||||
const char *lmap_level_ ## __LINE__ = lookup_level_by_id(input_level_ ## __LINE__); \
|
||||
char lmap_unknown_level ## __LINE__[32] = {0}; \
|
||||
if (lmap_level_ ## __LINE__ == NULL) { \
|
||||
snprintf(lmap_unknown_level ## __LINE__, sizeof(lmap_unknown_level ## __LINE__), \
|
||||
|
|
@ -252,47 +221,8 @@ static const char *lmap_get_string(const struct level_map *map, int level)
|
|||
} while(0)
|
||||
|
||||
|
||||
static int array_find_index(const char **array, int nmemb, const char *string)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nmemb; i++) {
|
||||
if (strcasecmp(array[i], string) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return nmemb;
|
||||
}
|
||||
|
||||
static void cmd_logging(char *buffer, int *buflen, int argc, char **argv)
|
||||
{
|
||||
static const char *modules[X52D_MOD_MAX] = {
|
||||
[X52D_MOD_CONFIG] = "config",
|
||||
[X52D_MOD_CLOCK] = "clock",
|
||||
[X52D_MOD_DEVICE] = "device",
|
||||
[X52D_MOD_IO] = "io",
|
||||
[X52D_MOD_LED] = "led",
|
||||
[X52D_MOD_MOUSE] = "mouse",
|
||||
[X52D_MOD_COMMAND] = "command",
|
||||
[X52D_MOD_CLIENT] = "client",
|
||||
[X52D_MOD_NOTIFY] = "notify",
|
||||
[X52D_MOD_KEYBOARD_LAYOUT] = "keyboard_layout",
|
||||
};
|
||||
|
||||
// This corresponds to the levels in pinelog
|
||||
static const struct level_map loglevels[] = {
|
||||
{PINELOG_LVL_NOTSET, "default"},
|
||||
{PINELOG_LVL_NONE, "none"},
|
||||
{PINELOG_LVL_FATAL, "fatal"},
|
||||
{PINELOG_LVL_ERROR, "error"},
|
||||
{PINELOG_LVL_WARNING, "warning"},
|
||||
{PINELOG_LVL_INFO, "info"},
|
||||
{PINELOG_LVL_DEBUG, "debug"},
|
||||
{PINELOG_LVL_TRACE, "trace"},
|
||||
{0, NULL},
|
||||
};
|
||||
|
||||
if (argc < 2) {
|
||||
ERR("Insufficient arguments for 'logging' command");
|
||||
return;
|
||||
|
|
@ -301,13 +231,13 @@ static void cmd_logging(char *buffer, int *buflen, int argc, char **argv)
|
|||
// logging show [module]
|
||||
MATCH(1, "show") {
|
||||
if (argc == 2) {
|
||||
DATA_LMAP(loglevels, pinelog_get_level(), "global");
|
||||
DATA_LMAP(pinelog_get_level(), "global");
|
||||
} else if (argc == 3) {
|
||||
int module = array_find_index(modules, X52D_MOD_MAX, argv[2]);
|
||||
if (module == X52D_MOD_MAX) {
|
||||
int module = lookup_module_by_name(argv[2]);
|
||||
if (module == INT_MAX) {
|
||||
ERR_fmt("Invalid module '%s'", argv[2]);
|
||||
} else {
|
||||
DATA_LMAP(loglevels, pinelog_get_module_level(module), argv[2]);
|
||||
DATA_LMAP(pinelog_get_module_level(module), argv[2]);
|
||||
}
|
||||
} else {
|
||||
ERR_fmt("Unexpected arguments for 'logging show' command; got %d, expected 2 or 3", argc);
|
||||
|
|
@ -319,7 +249,7 @@ static void cmd_logging(char *buffer, int *buflen, int argc, char **argv)
|
|||
// logging set [module] <level>
|
||||
MATCH(1, "set") {
|
||||
if (argc == 3) {
|
||||
int level = lmap_get_level(loglevels, argv[2], INT_MAX);
|
||||
int level = lookup_level_by_name(argv[2]);
|
||||
if (level == INT_MAX) {
|
||||
ERR_fmt("Unknown level '%s' for 'logging set' command", argv[2]);
|
||||
} else if (level == PINELOG_LVL_NOTSET) {
|
||||
|
|
@ -329,10 +259,10 @@ static void cmd_logging(char *buffer, int *buflen, int argc, char **argv)
|
|||
OK("logging", "set", argv[2]);
|
||||
}
|
||||
} else if (argc == 4) {
|
||||
int level = lmap_get_level(loglevels, argv[3], INT_MAX);
|
||||
int module = array_find_index(modules, X52D_MOD_MAX, argv[2]);
|
||||
int level = lookup_level_by_name(argv[3]);
|
||||
int module = lookup_module_by_name(argv[2]);
|
||||
|
||||
if (module == X52D_MOD_MAX) {
|
||||
if (module == INT_MAX) {
|
||||
ERR_fmt("Invalid module '%s'", argv[2]);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"_comment": "The configuration registry is a historic record of\nall configuration identifiers. Do NOT edit this file manually, or else, the\ncommunication protocol may break.",
|
||||
"sections": {
|
||||
"CLOCK": 1,
|
||||
"LED": 2,
|
||||
"BRIGHTNESS": 3,
|
||||
"MOUSE": 4,
|
||||
"PROFILES": 5
|
||||
},
|
||||
"options": {
|
||||
"CLOCK": {
|
||||
"ENABLED": 1,
|
||||
"PRIMARYISLOCAL": 2,
|
||||
"SECONDARY": 3,
|
||||
"TERTIARY": 4,
|
||||
"FORMATPRIMARY": 5,
|
||||
"FORMATSECONDARY": 6,
|
||||
"FORMATTERTIARY": 7,
|
||||
"DATEFORMAT": 8
|
||||
},
|
||||
"LED": {
|
||||
"FIRE": 1,
|
||||
"THROTTLE": 2,
|
||||
"A": 3,
|
||||
"B": 4,
|
||||
"D": 5,
|
||||
"E": 6,
|
||||
"T1": 7,
|
||||
"T2": 8,
|
||||
"T3": 9,
|
||||
"POV": 10,
|
||||
"CLUTCH": 11
|
||||
},
|
||||
"BRIGHTNESS": {
|
||||
"MFD": 1,
|
||||
"LED": 2
|
||||
},
|
||||
"MOUSE": {
|
||||
"ENABLED": 1,
|
||||
"SENSITIVITY": 2,
|
||||
"SPEED": 3,
|
||||
"REVERSESCROLL": 4,
|
||||
"ISOMETRICMODE": 5,
|
||||
"CURVEFACTOR": 6,
|
||||
"DEADZONE": 7
|
||||
},
|
||||
"PROFILES": {
|
||||
"DIRECTORY": 1,
|
||||
"CLUTCHENABLED": 2,
|
||||
"CLUTCHLATCHED": 3,
|
||||
"KEYBOARDLAYOUT": 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -26,19 +26,6 @@
|
|||
|
||||
#define X52D_MAX_CLIENTS 63
|
||||
|
||||
enum {
|
||||
X52D_MOD_CONFIG,
|
||||
X52D_MOD_CLOCK,
|
||||
X52D_MOD_DEVICE,
|
||||
X52D_MOD_IO,
|
||||
X52D_MOD_LED,
|
||||
X52D_MOD_MOUSE,
|
||||
X52D_MOD_COMMAND,
|
||||
X52D_MOD_CLIENT,
|
||||
X52D_MOD_NOTIFY,
|
||||
X52D_MOD_KEYBOARD_LAYOUT,
|
||||
|
||||
X52D_MOD_MAX
|
||||
};
|
||||
#include "module-map.h" // For module IDs
|
||||
|
||||
#endif // !defined X52D_CONST_H
|
||||
|
|
|
|||
|
|
@ -1,4 +1,33 @@
|
|||
# x52d (dep_config_h: Meson build-config.h; private API is daemon/config.h)
|
||||
config_defs = custom_target('config-defs',
|
||||
depend_files: ['x52d_map_config.py', 'x52d.conf'],
|
||||
input: [
|
||||
'x52d.conf',
|
||||
'config_registry.json'
|
||||
],
|
||||
output: [
|
||||
'config-defs.h',
|
||||
'config-defs.c',
|
||||
'config_defs.py'
|
||||
],
|
||||
command: [
|
||||
python, meson.current_source_dir() / 'x52d_map_config.py',
|
||||
'@INPUT0@', '@INPUT1@',
|
||||
'@OUTPUT0@', '@OUTPUT1@', '@OUTPUT2@'
|
||||
])
|
||||
|
||||
module_defs = custom_target('module-defs',
|
||||
depend_files: ['x52d_gen_module.py', 'module_defs.py'],
|
||||
output: ['module-map.h', 'module-map.c'],
|
||||
command: [python, meson.current_source_dir() / 'x52d_gen_module.py',
|
||||
'@OUTPUT0@', '@OUTPUT1@'])
|
||||
|
||||
slib_comm_defs = static_library('x52dcommdefs',
|
||||
[config_defs[1],
|
||||
module_defs[1],
|
||||
'name-id-map.c',
|
||||
])
|
||||
|
||||
libx52dcomm_version = '1.0.0'
|
||||
|
||||
libx52dcomm_sources = [
|
||||
|
|
@ -44,7 +73,7 @@ x52d_sources = [
|
|||
dep_threads = dependency('threads')
|
||||
|
||||
# Comm sources are compiled into x52d (same as Autotools); libx52dcomm is only for x52ctl.
|
||||
x52d_linkwith = [lib_libx52, lib_vkm, lib_libx52io]
|
||||
x52d_linkwith = [lib_libx52, lib_vkm, lib_libx52io, slib_comm_defs]
|
||||
x52d_deps = [dep_pinelog, dep_inih, dep_threads, dep_math, dep_intl, dep_config_h]
|
||||
x52d_cflags = []
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
"""Module name to identifier mapping"""
|
||||
|
||||
from enum import Enum
|
||||
|
||||
class Module(Enum):
|
||||
"""Module name to identifier"""
|
||||
CONFIG = 0
|
||||
CLOCK = 1
|
||||
DEVICE = 2
|
||||
IO = 3
|
||||
LED = 4
|
||||
MOUSE = 5
|
||||
COMMAND = 6
|
||||
CLIENT = 7
|
||||
NOTIFY = 8
|
||||
KEYBOARD_LAYOUT = 9
|
||||
|
||||
class LogLevel(Enum):
|
||||
"""Map log level names to pinelog levels"""
|
||||
# This is hard coded to the pinelog levels
|
||||
NOTSET = -2
|
||||
NONE = -1
|
||||
FATAL = 0
|
||||
ERROR = 1
|
||||
WARNING = 2
|
||||
INFO = 3
|
||||
DEBUG = 4
|
||||
TRACE = 5
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Name ID map - needed to map module/loglevel names to numeric v alues
|
||||
*
|
||||
* Copyright (C) 2026 Nirenjan Krishnan <nirenjan@nirenjan.org>
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "name-id-map.h"
|
||||
#include "module-map.h"
|
||||
|
||||
static int map_get_id(const struct name_id_map *map, const char *string)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; map[i].name != NULL; i++) {
|
||||
if (strcasecmp(map[i].name, string) == 0) {
|
||||
return map[i].id;
|
||||
}
|
||||
}
|
||||
|
||||
// We've broken out of the loop, return the current ID
|
||||
return map[i].id;
|
||||
}
|
||||
|
||||
static const char *map_get_name(const struct name_id_map *map, int id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; map[i].name != NULL; i++) {
|
||||
if (map[i].id == id) {
|
||||
return map[i].name;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int lookup_module_by_name(const char *name)
|
||||
{
|
||||
return map_get_id(module_map, name);
|
||||
}
|
||||
|
||||
const char * lookup_module_by_id(int id)
|
||||
{
|
||||
return map_get_name(module_map, id);
|
||||
}
|
||||
|
||||
int lookup_level_by_name(const char *name)
|
||||
{
|
||||
return map_get_id(loglevel_map, name);
|
||||
}
|
||||
|
||||
const char * lookup_level_by_id(int id)
|
||||
{
|
||||
return map_get_name(loglevel_map, id);
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Name ID map - needed to map module/loglevel names to numeric v alues
|
||||
*
|
||||
* Copyright (C) 2026 Nirenjan Krishnan <nirenjan@nirenjan.org>
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
*/
|
||||
|
||||
#ifndef NAME_ID_MAP_H
|
||||
#define NAME_ID_MAP_H
|
||||
|
||||
struct name_id_map {
|
||||
char *name;
|
||||
int id;
|
||||
};
|
||||
|
||||
extern const struct name_id_map module_map[];
|
||||
extern const struct name_id_map loglevel_map[];
|
||||
|
||||
#endif // !defined NAME_ID_MAP_H
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Generate the module name to map for use by the daemon"""
|
||||
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
import module_defs
|
||||
|
||||
def main():
|
||||
|
||||
if len(sys.argv) != 3:
|
||||
print("Usage: {sys.argv[0]} <output-header> <output-source>", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
with open(sys.argv[1], 'w', encoding='utf-8') as out_fd:
|
||||
# Generate the header
|
||||
print("// Autogenerated module/loglevel header - DO NOT EDIT\n",
|
||||
file=out_fd)
|
||||
|
||||
include_guard = os.path.basename(sys.argv[1]).replace('-', '_').replace('.', '_').upper()
|
||||
print(f"#ifndef {include_guard}", file=out_fd)
|
||||
print(f"#define {include_guard}\n", file=out_fd)
|
||||
|
||||
for mod in module_defs.Module:
|
||||
print(f"#define X52D_MOD_{mod.name} {mod.value}", file=out_fd)
|
||||
|
||||
print(f"#define X52D_MOD_GLOBAL 0xFF", file=out_fd)
|
||||
print(f"#define X52D_MOD_MAX {len(module_defs.Module)}\n", file=out_fd)
|
||||
|
||||
print(f"int lookup_module_by_name(const char *name);", file=out_fd)
|
||||
print(f"const char * lookup_module_by_id(int id);", file=out_fd)
|
||||
print(f"int lookup_level_by_name(const char *name);", file=out_fd)
|
||||
print(f"const char * lookup_level_by_id(int id);", file=out_fd)
|
||||
|
||||
print(f"\n#endif // !defined {include_guard}", file=out_fd)
|
||||
|
||||
with open(sys.argv[2], 'w', encoding='utf-8') as out_fd:
|
||||
print("// Autogenerated module/loglevel tables - DO NOT EDIT\n",
|
||||
file=out_fd)
|
||||
|
||||
print('#include <stddef.h>', file=out_fd)
|
||||
print('#include <limits.h>\n', file=out_fd)
|
||||
|
||||
print(f'#include "{os.path.basename(sys.argv[1])}"', file=out_fd)
|
||||
print('#include "name-id-map.h"\n', file=out_fd)
|
||||
|
||||
print('const struct name_id_map module_map[] = {', file=out_fd)
|
||||
for mod in module_defs.Module:
|
||||
print(f' {{ "{mod.name.lower()}", {mod.value} }},', file=out_fd)
|
||||
|
||||
print(' { NULL, INT_MAX }', file=out_fd)
|
||||
print('};\n', file=out_fd)
|
||||
|
||||
print('const struct name_id_map loglevel_map[] = {', file=out_fd)
|
||||
for level in module_defs.LogLevel:
|
||||
if level == module_defs.LogLevel.NOTSET:
|
||||
level_name = 'default'
|
||||
else:
|
||||
level_name = level.name.lower()
|
||||
|
||||
print(f' {{ "{level_name}", {level.value} }},', file=out_fd)
|
||||
|
||||
print(' { NULL, INT_MAX }', file=out_fd)
|
||||
|
||||
print('};\n', file=out_fd)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Read the default configuration file, and create ID enums for sections
|
||||
and options.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import configparser
|
||||
import json
|
||||
import os.path
|
||||
|
||||
from collections import defaultdict
|
||||
from itertools import count
|
||||
from pprint import pprint
|
||||
|
||||
class ConfigToEnum:
|
||||
"""ConfigToEnum scans a configuration file, and dumps the secions and
|
||||
options within a section as Python Enums"""
|
||||
|
||||
REGISTRY_COMMENT = """The configuration registry is a historic record of
|
||||
all configuration identifiers. Do NOT edit this file manually, or else, the
|
||||
communication protocol may break."""
|
||||
|
||||
def __init__(self, cfg_file, config_ids):
|
||||
"""Initialize the object"""
|
||||
self.config = configparser.ConfigParser(default_section=None, interpolation=None)
|
||||
self.config.optionxform = str
|
||||
self.config.read(cfg_file)
|
||||
|
||||
self.registry_file = config_ids
|
||||
|
||||
try:
|
||||
with open(self.registry_file, encoding='utf-8') as regfd:
|
||||
self.config_ids = json.load(regfd)
|
||||
except Exception:
|
||||
# On any error, ignore it and start with a clean slate
|
||||
self.config_ids = {}
|
||||
|
||||
self.sections = {}
|
||||
self.options = {}
|
||||
|
||||
def parse(self):
|
||||
"""Parse the config object and assign IDs"""
|
||||
self._parse_sections()
|
||||
for section in self.config.sections():
|
||||
self._parse_options(section)
|
||||
|
||||
def _parse_sections(self):
|
||||
"""Assign IDs to each section"""
|
||||
sections = {}
|
||||
unassigned = []
|
||||
for section in self.config.sections():
|
||||
section = section.upper()
|
||||
section_id = self.config_ids.get('sections', {}).get(section)
|
||||
if section_id is None:
|
||||
unassigned.append(section)
|
||||
else:
|
||||
sections[section] = section_id
|
||||
|
||||
if not sections:
|
||||
counter = count(1)
|
||||
else:
|
||||
counter = count(max(sections.values()) + 1)
|
||||
|
||||
sections.update({k:v for k, v in zip(unassigned, counter)})
|
||||
|
||||
orig_sections = self.config_ids.get('sections', {})
|
||||
sections.update({k:v for k, v in orig_sections.items() if k not in sections})
|
||||
|
||||
self.sections = sections
|
||||
|
||||
def _parse_options(self, section):
|
||||
options = {}
|
||||
unassigned = []
|
||||
for option in self.config.options(section):
|
||||
option = option.upper()
|
||||
section = section.upper()
|
||||
option_id = self.config_ids.get('options', {}).get(section, {}).get(option)
|
||||
if option_id is None:
|
||||
unassigned.append(option)
|
||||
else:
|
||||
options[option] = option_id
|
||||
|
||||
if not options:
|
||||
counter = count(1)
|
||||
else:
|
||||
counter = count(max(options.values()) + 1)
|
||||
|
||||
options.update({k:v for k, v in zip(unassigned, counter)})
|
||||
orig_options = self.config_ids.get('options', {}).get(section, {})
|
||||
|
||||
# Make sure that we have all the entries already
|
||||
options.update({k:v for k, v in orig_options.items() if k not in options})
|
||||
|
||||
self.options[section] = options
|
||||
|
||||
def save_registry(self):
|
||||
"""Save the generated registry"""
|
||||
registry = {
|
||||
"_comment": self.REGISTRY_COMMENT,
|
||||
"sections": self.sections,
|
||||
"options": self.options,
|
||||
}
|
||||
|
||||
with open(self.registry_file, 'w', encoding='utf-8') as regfd:
|
||||
json.dump(registry, regfd, indent=4)
|
||||
|
||||
def generate_c_definitions(self, output_header, output_source):
|
||||
"""Generate the C definitions"""
|
||||
with open(output_header, 'w', encoding='utf-8') as out_fd:
|
||||
include_guard = os.path.basename(output_header).replace('-', '_').replace('.', '_').upper()
|
||||
|
||||
print("// Autogenerated config identifiers - DO NOT EDIT\n", file=out_fd)
|
||||
print(f"#ifndef {include_guard}", file=out_fd)
|
||||
print(f"#define {include_guard}", file=out_fd)
|
||||
print(file=out_fd)
|
||||
|
||||
max_sec_val = max(self.sections.values()) + 1
|
||||
max_opt_val_global = 0
|
||||
for section, value in self.sections.items():
|
||||
print(f"#define CFG_SECTION_{section} {value}", file=out_fd)
|
||||
max_opt_val = max(self.options[section].values()) + 1
|
||||
max_opt_val_global = max(max_opt_val, max_opt_val_global)
|
||||
|
||||
for option, value in self.options[section].items():
|
||||
print(f"#define CFG_OPTION_{section}_{option} {value}", file=out_fd)
|
||||
|
||||
print(f"#define CFG_OPTION_{section}_MAX_OPTIONS {max_opt_val}\n", file=out_fd)
|
||||
|
||||
print(f"#define CFG_SECTION_MAX {max_sec_val}\n", file=out_fd)
|
||||
print(f"#define CFG_SECTION_MAX_OPT_VAL {max_opt_val_global}\n", file=out_fd)
|
||||
|
||||
print("extern const char * section_names[CFG_SECTION_MAX];", file=out_fd)
|
||||
print("extern const char * option_names[CFG_SECTION_MAX][CFG_SECTION_MAX_OPT_VAL];", file=out_fd)
|
||||
|
||||
print(f"#endif // !defined {include_guard}", file=out_fd)
|
||||
|
||||
with open(output_source, 'w', encoding='utf-8') as out_fd:
|
||||
print("// Autogenerated config string table - DO NOT EDIT\n", file=out_fd)
|
||||
print(f'#include "{os.path.basename(output_header)}"', file=out_fd)
|
||||
|
||||
print("const char * section_names[CFG_SECTION_MAX] = {", file=out_fd)
|
||||
for section, value in self.sections.items():
|
||||
print(f' [{value}] = "{section.lower()}",', file=out_fd)
|
||||
print("};\n", file=out_fd)
|
||||
|
||||
print("const char * options_names[CFG_SECTION_MAX][CFG_SECTION_MAX_OPT_VAL] = {", file=out_fd)
|
||||
for section, value in self.sections.items():
|
||||
print(f' [{value}] =', '{', file=out_fd)
|
||||
for option, value in self.options[section].items():
|
||||
print(f' [{value}] = "{option.lower()}",', file=out_fd)
|
||||
print(' },', file=out_fd)
|
||||
print("};\n", file=out_fd)
|
||||
|
||||
|
||||
def generate_py_definitions(self, output_file):
|
||||
"""Generate the Python definitions"""
|
||||
try:
|
||||
out_fd = open(output_file, 'w', encoding='utf-8')
|
||||
|
||||
print("'''Autogenerated config identifiers from x52d.conf'''", file=out_fd)
|
||||
print("# DO NOT EDIT\n", file=out_fd)
|
||||
print("from enum import Enum", file=out_fd)
|
||||
|
||||
print("\nclass Section(Enum):", file=out_fd)
|
||||
print(" '''Section identifiers'''", file=out_fd)
|
||||
for section, value in self.sections.items():
|
||||
print(f" {section} = {value}", file=out_fd)
|
||||
|
||||
for section in self.sections.keys():
|
||||
print(f"\nclass {section}(Enum):", file=out_fd)
|
||||
print(f" '''Section {section} identifiers'''", file=out_fd)
|
||||
for option, value in self.options[section].items():
|
||||
print(f" {option} = {value}", file=out_fd)
|
||||
|
||||
finally:
|
||||
out_fd.close()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Generate C enum and python enum for config")
|
||||
parser.add_argument('input_file')
|
||||
parser.add_argument('registry')
|
||||
parser.add_argument('output_c_header')
|
||||
parser.add_argument('output_c_strings')
|
||||
parser.add_argument('output_py_defs')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
c2e = ConfigToEnum(args.input_file, args.registry)
|
||||
c2e.parse()
|
||||
c2e.save_registry()
|
||||
|
||||
c2e.generate_c_definitions(args.output_c_header, args.output_c_strings)
|
||||
c2e.generate_py_definitions(args.output_py_defs)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -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-20 21:55-0700\n"
|
||||
"POT-Creation-Date: 2026-04-22 09:02-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"
|
||||
|
|
@ -285,89 +285,89 @@ msgstr ""
|
|||
msgid "Event @ %ld.%06ld: %s, value %d\n"
|
||||
msgstr ""
|
||||
|
||||
#: joytest/x52_test.c:97
|
||||
#: joytest/x52_test.c:99
|
||||
msgid "Test brightness scale (~ 1m)"
|
||||
msgstr ""
|
||||
|
||||
#: joytest/x52_test.c:98
|
||||
#: joytest/x52_test.c:100
|
||||
msgid "Test LED states (~ 45s)"
|
||||
msgstr ""
|
||||
|
||||
#: joytest/x52_test.c:99
|
||||
#: joytest/x52_test.c:101
|
||||
msgid "Test MFD string display (~ 30s)"
|
||||
msgstr ""
|
||||
|
||||
#: joytest/x52_test.c:100
|
||||
#: joytest/x52_test.c:102
|
||||
msgid "Test MFD displays all characters (~ 2m 15s)"
|
||||
msgstr ""
|
||||
|
||||
#: joytest/x52_test.c:101
|
||||
#: joytest/x52_test.c:103
|
||||
msgid "Test the blink and shift commands (< 10s)"
|
||||
msgstr ""
|
||||
|
||||
#: joytest/x52_test.c:102
|
||||
#: joytest/x52_test.c:104
|
||||
msgid "Test the clock commands (~1m)"
|
||||
msgstr ""
|
||||
|
||||
#: joytest/x52_test.c:126
|
||||
#: joytest/x52_test.c:128
|
||||
msgid ""
|
||||
"x52test is a suite of tests to write to the X52 Pro device\n"
|
||||
"and test the extra functionality available in the LEDs and MFD\n"
|
||||
msgstr ""
|
||||
|
||||
#: joytest/x52_test.c:130
|
||||
#: joytest/x52_test.c:132
|
||||
msgid "These tests take roughly 6 minutes to run"
|
||||
msgstr ""
|
||||
|
||||
#: joytest/x52_test.c:132
|
||||
#: joytest/x52_test.c:134
|
||||
msgid "Press Enter to begin the tests, press Ctrl-C to abort anytime"
|
||||
msgstr ""
|
||||
|
||||
#: joytest/x52_test.c:138
|
||||
#: joytest/x52_test.c:140
|
||||
#, c-format
|
||||
msgid "Unable to initialize X52 library: %s\n"
|
||||
msgstr ""
|
||||
|
||||
#: joytest/x52_test.c:153
|
||||
msgid "All tests completed successfully"
|
||||
msgstr ""
|
||||
|
||||
#: joytest/x52_test.c:155
|
||||
#, c-format
|
||||
msgid "Got error %s\n"
|
||||
msgid "All tests completed successfully"
|
||||
msgstr ""
|
||||
|
||||
#: joytest/x52_test.c:157
|
||||
#, c-format
|
||||
msgid "Got error %s\n"
|
||||
msgstr ""
|
||||
|
||||
#: joytest/x52_test.c:159
|
||||
#, c-format
|
||||
msgid "Received %s signal, quitting...\n"
|
||||
msgstr ""
|
||||
|
||||
#: joytest/x52_test.c:176
|
||||
#: joytest/x52_test.c:178
|
||||
msgid ""
|
||||
"These are the available tests with a description and\n"
|
||||
"approximate runtime. Not specifying any tests will run\n"
|
||||
"all the tests\n"
|
||||
msgstr ""
|
||||
|
||||
#: joytest/x52_test.c:180
|
||||
#: joytest/x52_test.c:182
|
||||
msgid "List of tests:"
|
||||
msgstr ""
|
||||
|
||||
#: joytest/x52_test.c:230
|
||||
#: joytest/x52_test.c:214
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Usage: %s [list of tests]\n"
|
||||
"\n"
|
||||
msgstr ""
|
||||
|
||||
#: joytest/x52_test.c:244
|
||||
#: joytest/x52_test.c:220
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Unrecognized test identifier: %s\n"
|
||||
"\n"
|
||||
msgstr ""
|
||||
|
||||
#: joytest/x52_test.c:257
|
||||
#: joytest/x52_test.c:231
|
||||
msgid "Not running any tests"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -599,41 +599,41 @@ msgstr ""
|
|||
msgid "Shutting down X52 clock manager thread"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/command.c:380
|
||||
#: daemon/command.c:310
|
||||
#, c-format
|
||||
msgid "Error reading from client %d: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/command.c:391
|
||||
#: daemon/command.c:321
|
||||
#, c-format
|
||||
msgid "Short write to client %d; expected %d bytes, wrote %d bytes"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/command.c:416
|
||||
#: daemon/command.c:346
|
||||
#, c-format
|
||||
msgid "Error %d during command loop: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/command.c:443
|
||||
#: daemon/command.c:373
|
||||
#, c-format
|
||||
msgid "Error creating command socket: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/command.c:451
|
||||
#: daemon/command.c:381
|
||||
#, c-format
|
||||
msgid "Error marking command socket as nonblocking: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/command.c:457
|
||||
#: daemon/command.c:387
|
||||
#, c-format
|
||||
msgid "Error listening on command socket: %s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/command.c:461
|
||||
#: daemon/command.c:391
|
||||
msgid "Starting command processing thread"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/command.c:479
|
||||
#: daemon/command.c:409
|
||||
msgid "Shutting down command processing thread"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
54
po/xx_PL.po
54
po/xx_PL.po
|
|
@ -7,7 +7,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-20 21:55-0700\n"
|
||||
"POT-Creation-Date: 2026-04-22 09:02-0700\n"
|
||||
"PO-Revision-Date: 2026-04-04 12:00-0700\n"
|
||||
"Last-Translator: Nirenjan Krishnan <nirenjan@gmail.com>\n"
|
||||
"Language-Team: Dummy Language for testing i18n\n"
|
||||
|
|
@ -285,31 +285,31 @@ msgstr "Estingtay (interruptay otay exitay)\n"
|
|||
msgid "Event @ %ld.%06ld: %s, value %d\n"
|
||||
msgstr "Eventay @ %ld.%06ld: %s, aluevay %d\n"
|
||||
|
||||
#: joytest/x52_test.c:97
|
||||
#: joytest/x52_test.c:99
|
||||
msgid "Test brightness scale (~ 1m)"
|
||||
msgstr "Esttay ightnessbray alescay (~ 1m)"
|
||||
|
||||
#: joytest/x52_test.c:98
|
||||
#: joytest/x52_test.c:100
|
||||
msgid "Test LED states (~ 45s)"
|
||||
msgstr "Esstay EDLay atesstay (~ 45s)"
|
||||
|
||||
#: joytest/x52_test.c:99
|
||||
#: joytest/x52_test.c:101
|
||||
msgid "Test MFD string display (~ 30s)"
|
||||
msgstr "Esttay MFDay ingstray isplayday (~ 30s)"
|
||||
|
||||
#: joytest/x52_test.c:100
|
||||
#: joytest/x52_test.c:102
|
||||
msgid "Test MFD displays all characters (~ 2m 15s)"
|
||||
msgstr "Esttay MFDay isplaysday allay aracterschay (~ 2m 15s)"
|
||||
|
||||
#: joytest/x52_test.c:101
|
||||
#: joytest/x52_test.c:103
|
||||
msgid "Test the blink and shift commands (< 10s)"
|
||||
msgstr "Esttay ethay inkblay anday iftshay ommandscay (< 10s)"
|
||||
|
||||
#: joytest/x52_test.c:102
|
||||
#: joytest/x52_test.c:104
|
||||
msgid "Test the clock commands (~1m)"
|
||||
msgstr "Esttay ethay ockclay ommandscay (~1m)"
|
||||
|
||||
#: joytest/x52_test.c:126
|
||||
#: joytest/x52_test.c:128
|
||||
msgid ""
|
||||
"x52test is a suite of tests to write to the X52 Pro device\n"
|
||||
"and test the extra functionality available in the LEDs and MFD\n"
|
||||
|
|
@ -318,36 +318,36 @@ msgstr ""
|
|||
"X52 Pro eviceday anday esttay ethay extray unctionalityfay\n"
|
||||
"availableay inay ethay EDsLay anday FDMay\n"
|
||||
|
||||
#: joytest/x52_test.c:130
|
||||
#: joytest/x52_test.c:132
|
||||
msgid "These tests take roughly 6 minutes to run"
|
||||
msgstr "Esethay eststay aketay oughlyray 6 inutesmay otay unray"
|
||||
|
||||
#: joytest/x52_test.c:132
|
||||
#: joytest/x52_test.c:134
|
||||
msgid "Press Enter to begin the tests, press Ctrl-C to abort anytime"
|
||||
msgstr ""
|
||||
"Esspray Enteray otay eginbay ethay eststay, esspray Ctrl-C otay abortay "
|
||||
"anytimeay"
|
||||
|
||||
#: joytest/x52_test.c:138
|
||||
#: joytest/x52_test.c:140
|
||||
#, c-format
|
||||
msgid "Unable to initialize X52 library: %s\n"
|
||||
msgstr "Unableay otay initializeay X52 ibrarylay: %s\n"
|
||||
|
||||
#: joytest/x52_test.c:153
|
||||
#: joytest/x52_test.c:155
|
||||
msgid "All tests completed successfully"
|
||||
msgstr "Allay eststay ompletedcay uccessfullysay"
|
||||
|
||||
#: joytest/x52_test.c:155
|
||||
#: joytest/x52_test.c:157
|
||||
#, c-format
|
||||
msgid "Got error %s\n"
|
||||
msgstr "Otgay erroray %s\n"
|
||||
|
||||
#: joytest/x52_test.c:157
|
||||
#: joytest/x52_test.c:159
|
||||
#, c-format
|
||||
msgid "Received %s signal, quitting...\n"
|
||||
msgstr "Eceivedray %s ignalsay, uittingqay...\n"
|
||||
|
||||
#: joytest/x52_test.c:176
|
||||
#: joytest/x52_test.c:178
|
||||
msgid ""
|
||||
"These are the available tests with a description and\n"
|
||||
"approximate runtime. Not specifying any tests will run\n"
|
||||
|
|
@ -358,18 +358,18 @@ msgstr ""
|
|||
"ecifyingspay anyay eststay illway unray allay ethay\n"
|
||||
"eststay\n"
|
||||
|
||||
#: joytest/x52_test.c:180
|
||||
#: joytest/x52_test.c:182
|
||||
msgid "List of tests:"
|
||||
msgstr "Istlay ofay eststay:"
|
||||
|
||||
#: joytest/x52_test.c:230
|
||||
#: joytest/x52_test.c:214
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Usage: %s [list of tests]\n"
|
||||
"\n"
|
||||
msgstr "Usageay: %s [istlay ofay eststay]\n"
|
||||
|
||||
#: joytest/x52_test.c:244
|
||||
#: joytest/x52_test.c:220
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Unrecognized test identifier: %s\n"
|
||||
|
|
@ -378,7 +378,7 @@ msgstr ""
|
|||
"Unrecognizeday esttay identifieray: %s\n"
|
||||
"\n"
|
||||
|
||||
#: joytest/x52_test.c:257
|
||||
#: joytest/x52_test.c:231
|
||||
msgid "Not running any tests"
|
||||
msgstr "Otnay unningray anyay eststay"
|
||||
|
||||
|
|
@ -645,42 +645,42 @@ msgstr "Erroray %d initializingay ockclay eadthray: %s"
|
|||
msgid "Shutting down X52 clock manager thread"
|
||||
msgstr "Uttingshay ownday X52 ockclay anagermay eadthray"
|
||||
|
||||
#: daemon/command.c:380
|
||||
#: daemon/command.c:310
|
||||
#, c-format
|
||||
msgid "Error reading from client %d: %s"
|
||||
msgstr "Erroray eadingray omfray ientclay %d: %s"
|
||||
|
||||
#: daemon/command.c:391
|
||||
#: daemon/command.c:321
|
||||
#, c-format
|
||||
msgid "Short write to client %d; expected %d bytes, wrote %d bytes"
|
||||
msgstr ""
|
||||
"Ortshay itewray otay ientclay %d; expecteday %d ytesbay, otewray %d ytesbay"
|
||||
|
||||
#: daemon/command.c:416
|
||||
#: daemon/command.c:346
|
||||
#, c-format
|
||||
msgid "Error %d during command loop: %s"
|
||||
msgstr "Erroray %d uringday ommandcay ooplay: %s"
|
||||
|
||||
#: daemon/command.c:443
|
||||
#: daemon/command.c:373
|
||||
#, c-format
|
||||
msgid "Error creating command socket: %s"
|
||||
msgstr "Erroray eatingcray ommandcay ocketsay: %s"
|
||||
|
||||
#: daemon/command.c:451
|
||||
#: daemon/command.c:381
|
||||
#, c-format
|
||||
msgid "Error marking command socket as nonblocking: %s"
|
||||
msgstr "Erroray arkingmay ommandcay ocketsay asay onblockingnay: %s"
|
||||
|
||||
#: daemon/command.c:457
|
||||
#: daemon/command.c:387
|
||||
#, c-format
|
||||
msgid "Error listening on command socket: %s"
|
||||
msgstr "Erroray isteninglay onay ommandcay ocketsay: %s"
|
||||
|
||||
#: daemon/command.c:461
|
||||
#: daemon/command.c:391
|
||||
msgid "Starting command processing thread"
|
||||
msgstr "Artingstay ommandcay ocessingpray eadthray"
|
||||
|
||||
#: daemon/command.c:479
|
||||
#: daemon/command.c:409
|
||||
msgid "Shutting down command processing thread"
|
||||
msgstr "Uttingshay ownday ommandcay ocessingpray eadthray"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue