Compose: add iterator API

Allow users to iterate the entries in a compose table. This is useful
for other projects which want programmable access to the sequences,
without having to write their own parser.

- New API:
  - `xkb_compose_table_entry_sequence`;
  - `xkb_compose_table_entry_keysym`;
  - `xkb_compose_table_entry_utf8`;
  - `xkb_compose_table_iterator_new`;
  - `xkb_compose_table_iterator_free`;
  - `xkb_compose_table_iterator_next`.
- Add tests in `test/compose.c`.
- Add benchmark for compose traversal.
- `tools/compose.c`:
  - Print entries instead of just validating them.
  - Add `--file` option.
  - TODO: make this tool part of the xkbcli commands.

Co-authored-by: Pierre Le Marre <dev@wismill.eu>
Co-authored-by: Ran Benita <ran@unusedvar.com>
Signed-off-by: Ran Benita <ran@unusedvar.com>
master
Ran Benita 2023-09-25 11:41:48 +02:00 committed by Wismill
parent c0065c95a4
commit a17701327e
11 changed files with 581 additions and 11 deletions

88
bench/compose-traversal.c Normal file
View File

@ -0,0 +1,88 @@
/*
* Copyright © 2023 Pierre Le Marre
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <time.h>
#include "xkbcommon/xkbcommon-compose.h"
#include "../test/test.h"
#include "bench.h"
#define BENCHMARK_ITERATIONS 1000
int
main(void)
{
struct xkb_context *ctx;
char *path;
FILE *file;
struct xkb_compose_table *table;
struct xkb_compose_table_iterator *iter;
struct xkb_compose_table_entry *entry;
struct bench bench;
char *elapsed;
ctx = test_get_context(CONTEXT_NO_FLAG);
assert(ctx);
path = test_get_path("locale/en_US.UTF-8/Compose");
file = fopen(path, "rb");
if (file == NULL) {
perror(path);
free(path);
xkb_context_unref(ctx);
return -1;
}
free(path);
xkb_context_set_log_level(ctx, XKB_LOG_LEVEL_CRITICAL);
xkb_context_set_log_verbosity(ctx, 0);
table = xkb_compose_table_new_from_file(ctx, file, "",
XKB_COMPOSE_FORMAT_TEXT_V1,
XKB_COMPOSE_COMPILE_NO_FLAGS);
fclose(file);
assert(table);
bench_start(&bench);
for (int i = 0; i < BENCHMARK_ITERATIONS; i++) {
iter = xkb_compose_table_iterator_new(table);
while ((entry = xkb_compose_table_iterator_next(iter))) {
assert (entry);
}
xkb_compose_table_iterator_free(iter);
}
bench_stop(&bench);
xkb_compose_table_unref(table);
elapsed = bench_elapsed_str(&bench);
fprintf(stderr, "traversed %d compose tables in %ss\n",
BENCHMARK_ITERATIONS, elapsed);
free(elapsed);
xkb_context_unref(ctx);
return 0;
}

View File

@ -299,6 +299,143 @@ xkb_compose_table_ref(struct xkb_compose_table *table);
void void
xkb_compose_table_unref(struct xkb_compose_table *table); xkb_compose_table_unref(struct xkb_compose_table *table);
/**
* @struct xkb_compose_table_entry
* Opaque Compose table entry object.
*
* Represents a single entry in a Compose file in the iteration API.
* It is immutable.
*
* @sa xkb_compose_table_iterator_new
* @since 1.6.0
*/
struct xkb_compose_table_entry;
/**
* Get the left-hand keysym sequence of a Compose table entry.
*
* For example, given the following entry:
*
* ```
* <dead_tilde> <space> : "~" asciitilde # TILDE
* ```
*
* it will return `{XKB_KEY_dead_tilde, XKB_KEY_space}`.
*
* @param[in] entry The compose table entry object to process.
*
* @param[out] sequence_length Number of keysyms in the sequence.
*
* @returns The array of left-hand side keysyms. The number of keysyms
* is returned in the @p sequence_length out-parameter.
*
* @memberof xkb_compose_table_entry
* @since 1.6.0
*/
const xkb_keysym_t *
xkb_compose_table_entry_sequence(struct xkb_compose_table_entry *entry,
size_t *sequence_length);
/**
* Get the right-hand result keysym of a Compose table entry.
*
* For example, given the following entry:
*
* ```
* <dead_tilde> <space> : "~" asciitilde # TILDE
* ```
*
* it will return `XKB_KEY_asciitilde`.
*
* The keysym is optional; if the entry does not specify a keysym,
* returns `XKB_KEY_NoSymbol`.
*
* @memberof xkb_compose_table_entry
* @since 1.6.0
*/
xkb_keysym_t
xkb_compose_table_entry_keysym(struct xkb_compose_table_entry *entry);
/**
* Get the right-hand result string of a Compose table entry.
*
* The string is UTF-8 encoded and NULL-terminated.
*
* For example, given the following entry:
*
* ```
* <dead_tilde> <space> : "~" asciitilde # TILDE
* ```
*
* it will return `"~"`.
*
* The string is optional; if the entry does not specify a string,
* returns the empty string.
*
* @memberof xkb_compose_table_entry
* @since 1.6.0
*/
const char *
xkb_compose_table_entry_utf8(struct xkb_compose_table_entry *entry);
/**
* @struct xkb_compose_table_iterator
* Iterator over a compose tables entries.
*
* @sa xkb_compose_table_iterator_new()
* @since 1.6.0
*/
struct xkb_compose_table_iterator;
/**
* Create a new iterator for a compose table.
*
* Intended use:
*
* ```c
* struct xkb_compose_table_iterator *iter = xkb_compose_table_iterator_new(compose_table);
* struct xkb_compose_table_entry *entry;
* while ((entry = xkb_compose_table_iterator_next(iter))) {
* // ...
* }
* xkb_compose_table_iterator_free(iter);
* ```
*
* @returns A new compose table iterator, or `NULL` on failure.
*
* @memberof xkb_compose_table_iterator
* @sa xkb_compose_table_iterator_free()
* @since 1.6.0
*/
struct xkb_compose_table_iterator *
xkb_compose_table_iterator_new(struct xkb_compose_table *table);
/**
* Free a compose iterator.
*
* @memberof xkb_compose_table_iterator
* @since 1.6.0
*/
void
xkb_compose_table_iterator_free(struct xkb_compose_table_iterator *iter);
/**
* Get the next compose entry from a compose table iterator.
*
* The entries are returned in lexicographic order of the left-hand
* side of entries. This does not correspond to the order in which
* the entries appear in the Compose file.
*
* @attention The return value is valid until the next call to this function.
*
* Returns `NULL` in case there is no more entries.
*
* @memberof xkb_compose_table_iterator
* @since 1.6.0
*/
struct xkb_compose_table_entry *
xkb_compose_table_iterator_next(struct xkb_compose_table_iterator *iter);
/** Flags for compose state creation. */ /** Flags for compose state creation. */
enum xkb_compose_state_flags { enum xkb_compose_state_flags {
/** Do not apply any flags. */ /** Do not apply any flags. */

View File

@ -804,6 +804,11 @@ benchmark(
executable('bench-compose', 'bench/compose.c', dependencies: test_dep), executable('bench-compose', 'bench/compose.c', dependencies: test_dep),
env: bench_env, env: bench_env,
) )
benchmark(
'compose-traversal',
executable('bench-compose-traversal', 'bench/compose-traversal.c', dependencies: test_dep),
env: bench_env,
)
benchmark( benchmark(
'atom', 'atom',
executable('bench-atom', 'bench/atom.c', dependencies: test_dep), executable('bench-atom', 'bench/atom.c', dependencies: test_dep),

View File

@ -63,9 +63,6 @@ OR PERFORMANCE OF THIS SOFTWARE.
#include "utf8.h" #include "utf8.h"
#include "parser.h" #include "parser.h"
#define MAX_LHS_LEN 10
#define MAX_INCLUDE_DEPTH 5
/* /*
* Grammar adapted from libX11/modules/im/ximcp/imLcPrs.c. * Grammar adapted from libX11/modules/im/ximcp/imLcPrs.c.
* See also the XCompose(5) manpage. * See also the XCompose(5) manpage.

View File

@ -24,6 +24,9 @@
#ifndef COMPOSE_PARSER_H #ifndef COMPOSE_PARSER_H
#define COMPOSE_PARSER_H #define COMPOSE_PARSER_H
#define MAX_LHS_LEN 10
#define MAX_INCLUDE_DEPTH 5
bool bool
parse_string(struct xkb_compose_table *table, parse_string(struct xkb_compose_table *table,
const char *string, size_t len, const char *string, size_t len,

View File

@ -27,6 +27,7 @@
#include "table.h" #include "table.h"
#include "parser.h" #include "parser.h"
#include "paths.h" #include "paths.h"
#include "xkbcommon/xkbcommon.h"
static struct xkb_compose_table * static struct xkb_compose_table *
xkb_compose_table_new(struct xkb_context *ctx, xkb_compose_table_new(struct xkb_context *ctx,
@ -228,3 +229,158 @@ found_path:
free(path); free(path);
return table; return table;
} }
XKB_EXPORT const xkb_keysym_t *
xkb_compose_table_entry_sequence(struct xkb_compose_table_entry *entry,
size_t *sequence_length)
{
*sequence_length = entry->sequence_length;
return entry->sequence;
}
XKB_EXPORT xkb_keysym_t
xkb_compose_table_entry_keysym(struct xkb_compose_table_entry *entry)
{
return entry->keysym;
}
XKB_EXPORT const char *
xkb_compose_table_entry_utf8(struct xkb_compose_table_entry *entry)
{
return entry->utf8;
}
enum node_direction {
NODE_LEFT = 0,
NODE_DOWN,
NODE_RIGHT,
NODE_UP
};
struct xkb_compose_table_iterator_cursor {
uint32_t node_offset:30; /* WARNING: ensure it fits MAX_COMPOSE_NODES */
uint8_t direction:2; /* enum node_direction: current direction
* traversing the tree */
};
struct xkb_compose_table_iterator {
struct xkb_compose_table *table;
/* Current entry */
struct xkb_compose_table_entry entry;
/* Stack of pending nodes to process */
darray(struct xkb_compose_table_iterator_cursor) cursors;
};
XKB_EXPORT struct xkb_compose_table_iterator *
xkb_compose_table_iterator_new(struct xkb_compose_table *table)
{
struct xkb_compose_table_iterator *iter;
struct xkb_compose_table_iterator_cursor cursor;
xkb_keysym_t *sequence;
iter = calloc(1, sizeof(*iter));
if (!iter) {
return NULL;
}
iter->table = xkb_compose_table_ref(table);
sequence = calloc(MAX_LHS_LEN, sizeof(xkb_keysym_t));
if (!sequence) {
free(iter);
return NULL;
}
iter->entry.sequence = sequence;
iter->entry.sequence_length = 0;
darray_init(iter->cursors);
cursor.direction = NODE_LEFT;
/* Offset 0 is a dummy null entry, skip it. */
cursor.node_offset = 1;
darray_append(iter->cursors, cursor);
return iter;
}
XKB_EXPORT void
xkb_compose_table_iterator_free(struct xkb_compose_table_iterator *iter)
{
xkb_compose_table_unref(iter->table);
darray_free(iter->cursors);
free(iter->entry.sequence);
free(iter);
}
XKB_EXPORT struct xkb_compose_table_entry *
xkb_compose_table_iterator_next(struct xkb_compose_table_iterator *iter)
{
/*
* This function takes the following recursive traversal function,
* and makes it non-recursive and resumable. The iter->cursors stack
* is analogous to the call stack, and cursor->direction to the
* instruction pointer of a stack frame.
*
* traverse(xkb_keysym_t *sequence, size_t sequence_length, uint16_t p) {
* if (!p) return
* // cursor->direction == NODE_LEFT
* node = &darray_item(table->nodes, p)
* traverse(sequence, sequence_length, node->lokid)
* // cursor->direction == NODE_DOWN
* sequence[sequence_length++] = node->keysym
* if (node->is_leaf)
* emit(sequence, sequence_length, node->leaf.keysym, table->utf[node->leaf.utf8])
* else
* traverse(sequence, sequence_length, node->internal.eqkid)
* sequence_length--
* // cursor->direction == NODE_RIGHT
* traverse(sequence, sequence_length, node->hikid)
* // cursor->direction == NODE_UP
* }
*/
struct xkb_compose_table_iterator_cursor *cursor;
const struct compose_node *node;
while (!darray_empty(iter->cursors)) {
cursor = &darray_item(iter->cursors, darray_size(iter->cursors) - 1);
node = &darray_item(iter->table->nodes, cursor->node_offset);
switch (cursor->direction) {
case NODE_LEFT:
cursor->direction = NODE_DOWN;
if (node->lokid) {
struct xkb_compose_table_iterator_cursor new_cursor = {node->lokid, NODE_LEFT};
darray_append(iter->cursors, new_cursor);
}
break;
case NODE_DOWN:
cursor->direction = NODE_RIGHT;
assert (iter->entry.sequence_length <= MAX_LHS_LEN);
iter->entry.sequence[iter->entry.sequence_length] = node->keysym;
iter->entry.sequence_length++;
if (node->is_leaf) {
iter->entry.keysym = node->leaf.keysym;
iter->entry.utf8 = &darray_item(iter->table->utf8, node->leaf.utf8);
return &iter->entry;
} else {
struct xkb_compose_table_iterator_cursor new_cursor = {node->internal.eqkid, NODE_LEFT};
darray_append(iter->cursors, new_cursor);
}
break;
case NODE_RIGHT:
cursor->direction = NODE_UP;
iter->entry.sequence_length--;
if (node->hikid) {
struct xkb_compose_table_iterator_cursor new_cursor = {node->hikid, NODE_LEFT};
darray_append(iter->cursors, new_cursor);
}
break;
case NODE_UP:
darray_remove_last(iter->cursors);
break;
}
}
return NULL;
}

View File

@ -119,4 +119,11 @@ struct xkb_compose_table {
darray(struct compose_node) nodes; darray(struct compose_node) nodes;
}; };
struct xkb_compose_table_entry {
xkb_keysym_t *sequence;
size_t sequence_length;
xkb_keysym_t keysym;
const char *utf8;
};
#endif #endif

View File

@ -114,6 +114,11 @@ typedef darray (unsigned long) darray_ulong;
#define darray_concat(arr_to, arr_from) \ #define darray_concat(arr_to, arr_from) \
darray_append_items((arr_to), (arr_from).item, (arr_from).size) darray_append_items((arr_to), (arr_from).item, (arr_from).size)
/*** Removal ***/
/* Warning: Do not call darray_remove_last on an empty darray. */
#define darray_remove_last(arr) (--(arr).size)
/*** String buffer ***/ /*** String buffer ***/
#define darray_append_string(arr, str) do { \ #define darray_append_string(arr, str) do { \

View File

@ -580,6 +580,110 @@ test_override(struct xkb_context *ctx)
XKB_KEY_NoSymbol)); XKB_KEY_NoSymbol));
} }
static bool
test_eq_entry_va(struct xkb_compose_table_entry *entry, xkb_keysym_t keysym_ref, const char *utf8_ref, va_list ap)
{
assert (entry != NULL);
assert (xkb_compose_table_entry_keysym(entry) == keysym_ref);
const char *utf8 = xkb_compose_table_entry_utf8(entry);
assert (utf8 && utf8_ref && strcmp(utf8, utf8_ref) == 0);
size_t nsyms;
const xkb_keysym_t *sequence = xkb_compose_table_entry_sequence(entry, &nsyms);
xkb_keysym_t keysym;
for (unsigned k = 0; ; k++) {
keysym = va_arg(ap, xkb_keysym_t);
if (keysym == XKB_KEY_NoSymbol) {
return (k == nsyms - 1);
}
assert (k < nsyms);
assert (keysym == sequence[k]);
}
}
static bool
test_eq_entry(struct xkb_compose_table_entry *entry, xkb_keysym_t keysym, const char *utf8, ...)
{
va_list ap;
bool ok;
va_start(ap, utf8);
ok = test_eq_entry_va(entry, keysym, utf8, ap);
va_end(ap);
return ok;
}
static void
test_traverse(struct xkb_context *ctx)
{
struct xkb_compose_table *table;
const char *buffer = "<dead_circumflex> <dead_circumflex> : \"foo\" X\n"
"<Ahook> <x> : \"foobar\"\n"
"<Multi_key> <o> <e> : oe\n"
"<dead_circumflex> <e> : \"bar\" Y\n"
"<Multi_key> <a> <e> : \"æ\" ae\n"
"<dead_circumflex> <a> : \"baz\" Z\n"
"<dead_acute> <e> : \"é\" eacute\n"
"<Multi_key> <a> <a> <c>: \"aac\"\n"
"<Multi_key> <a> <a> <b>: \"aab\"\n"
"<Multi_key> <a> <a> <a>: \"aaa\"\n";
table = xkb_compose_table_new_from_buffer(ctx, buffer, strlen(buffer), "",
XKB_COMPOSE_FORMAT_TEXT_V1,
XKB_COMPOSE_COMPILE_NO_FLAGS);
assert(table);
struct xkb_compose_table_iterator *iter = xkb_compose_table_iterator_new(table);
test_eq_entry(xkb_compose_table_iterator_next(iter),
XKB_KEY_eacute, "é",
XKB_KEY_dead_acute, XKB_KEY_e, XKB_KEY_NoSymbol);
test_eq_entry(xkb_compose_table_iterator_next(iter),
XKB_KEY_Z, "baz",
XKB_KEY_dead_circumflex, XKB_KEY_a, XKB_KEY_NoSymbol);
test_eq_entry(xkb_compose_table_iterator_next(iter),
XKB_KEY_Y, "bar",
XKB_KEY_dead_circumflex, XKB_KEY_e, XKB_KEY_NoSymbol);
test_eq_entry(xkb_compose_table_iterator_next(iter),
XKB_KEY_X, "foo",
XKB_KEY_dead_circumflex, XKB_KEY_dead_circumflex, XKB_KEY_NoSymbol);
test_eq_entry(xkb_compose_table_iterator_next(iter),
XKB_KEY_NoSymbol, "aaa",
XKB_KEY_Multi_key, XKB_KEY_a, XKB_KEY_a, XKB_KEY_a, XKB_KEY_NoSymbol);
test_eq_entry(xkb_compose_table_iterator_next(iter),
XKB_KEY_NoSymbol, "aab",
XKB_KEY_Multi_key, XKB_KEY_a, XKB_KEY_a, XKB_KEY_b, XKB_KEY_NoSymbol);
test_eq_entry(xkb_compose_table_iterator_next(iter),
XKB_KEY_NoSymbol, "aac",
XKB_KEY_Multi_key, XKB_KEY_a, XKB_KEY_a, XKB_KEY_c, XKB_KEY_NoSymbol);
test_eq_entry(xkb_compose_table_iterator_next(iter),
XKB_KEY_ae, "æ",
XKB_KEY_Multi_key, XKB_KEY_a, XKB_KEY_e, XKB_KEY_NoSymbol);
test_eq_entry(xkb_compose_table_iterator_next(iter),
XKB_KEY_oe, "",
XKB_KEY_Multi_key, XKB_KEY_o, XKB_KEY_e, XKB_KEY_NoSymbol);
test_eq_entry(xkb_compose_table_iterator_next(iter),
XKB_KEY_NoSymbol, "foobar",
XKB_KEY_Ahook, XKB_KEY_x, XKB_KEY_NoSymbol);
assert (xkb_compose_table_iterator_next(iter) == NULL);
xkb_compose_table_iterator_free(iter);
xkb_compose_table_unref(table);
}
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
@ -612,6 +716,7 @@ main(int argc, char *argv[])
test_modifier_syntax(ctx); test_modifier_syntax(ctx);
test_include(ctx); test_include(ctx);
test_override(ctx); test_override(ctx);
test_traverse(ctx);
xkb_context_unref(ctx); xkb_context_unref(ctx);
return 0; return 0;

View File

@ -29,21 +29,49 @@
#include <stdlib.h> #include <stdlib.h>
#include "xkbcommon/xkbcommon.h" #include "xkbcommon/xkbcommon.h"
#include "xkbcommon/xkbcommon-keysyms.h"
#include "xkbcommon/xkbcommon-compose.h" #include "xkbcommon/xkbcommon-compose.h"
static void static void
usage(FILE *fp, char *progname) usage(FILE *fp, char *progname)
{ {
fprintf(fp, fprintf(fp,
"Usage: %s [--locale LOCALE | --locale-from-env | --locale-from-setlocale]\n", "Usage: %s [--file FILE] [--locale LOCALE | --locale-from-env | --locale-from-setlocale]\n",
progname); progname);
fprintf(fp, fprintf(fp,
" --file - specify a file to load\n"
" --locale - specify the locale directly\n" " --locale - specify the locale directly\n"
" --locale-from-env - get the locale from the LC_ALL/LC_CTYPE/LANG environment variables (falling back to C)\n" " --locale-from-env - get the locale from the LC_ALL/LC_CTYPE/LANG environment variables (falling back to C)\n"
" --locale-from-setlocale - get the locale using setlocale(3)\n" " --locale-from-setlocale - get the locale using setlocale(3)\n"
); );
} }
static void
print_compose_table_entry(struct xkb_compose_table_entry *entry)
{
size_t nsyms;
const xkb_keysym_t *syms = xkb_compose_table_entry_sequence(entry, &nsyms);
char buf[128];
for (size_t i = 0; i < nsyms; i++) {
xkb_keysym_get_name(syms[i], buf, sizeof(buf));
printf("<%s>", buf);
if (i + 1 < nsyms) {
printf(" ");
}
}
printf(":");
const char *utf8 = xkb_compose_table_entry_utf8(entry);
if (*utf8 != '\0') {
printf(" \"%s\"", utf8);
}
const xkb_keysym_t keysym = xkb_compose_table_entry_keysym(entry);
if (keysym != XKB_KEY_NoSymbol) {
xkb_keysym_get_name(keysym, buf, sizeof(buf));
printf(" %s", buf);
}
printf("\n");
}
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
@ -51,12 +79,16 @@ main(int argc, char *argv[])
struct xkb_context *ctx = NULL; struct xkb_context *ctx = NULL;
struct xkb_compose_table *compose_table = NULL; struct xkb_compose_table *compose_table = NULL;
const char *locale = NULL; const char *locale = NULL;
const char *path = NULL;
enum xkb_compose_format format = XKB_COMPOSE_FORMAT_TEXT_V1;
enum options { enum options {
OPT_FILE,
OPT_LOCALE, OPT_LOCALE,
OPT_LOCALE_FROM_ENV, OPT_LOCALE_FROM_ENV,
OPT_LOCALE_FROM_SETLOCALE, OPT_LOCALE_FROM_SETLOCALE,
}; };
static struct option opts[] = { static struct option opts[] = {
{"file", required_argument, 0, OPT_FILE},
{"locale", required_argument, 0, OPT_LOCALE}, {"locale", required_argument, 0, OPT_LOCALE},
{"locale-from-env", no_argument, 0, OPT_LOCALE_FROM_ENV}, {"locale-from-env", no_argument, 0, OPT_LOCALE_FROM_ENV},
{"locale-from-setlocale", no_argument, 0, OPT_LOCALE_FROM_SETLOCALE}, {"locale-from-setlocale", no_argument, 0, OPT_LOCALE_FROM_SETLOCALE},
@ -74,6 +106,9 @@ main(int argc, char *argv[])
break; break;
switch (opt) { switch (opt) {
case OPT_FILE:
path = optarg;
break;
case OPT_LOCALE: case OPT_LOCALE:
locale = optarg; locale = optarg;
break; break;
@ -108,18 +143,40 @@ main(int argc, char *argv[])
goto out; goto out;
} }
compose_table = if (path != NULL) {
xkb_compose_table_new_from_locale(ctx, locale, FILE *file = fopen(path, "rb");
XKB_COMPOSE_COMPILE_NO_FLAGS); if (file == NULL) {
if (!compose_table) { perror(path);
fprintf(stderr, "Couldn't create compose from locale\n"); goto file_error;
goto out; }
compose_table =
xkb_compose_table_new_from_file(ctx, file, locale, format,
XKB_COMPOSE_COMPILE_NO_FLAGS);
fclose(file);
if (!compose_table) {
fprintf(stderr, "Couldn't create compose from file: %s\n", path);
goto out;
}
} else {
compose_table =
xkb_compose_table_new_from_locale(ctx, locale,
XKB_COMPOSE_COMPILE_NO_FLAGS);
if (!compose_table) {
fprintf(stderr, "Couldn't create compose from locale\n");
goto out;
}
} }
printf("Compiled successfully from locale %s\n", locale); struct xkb_compose_table_iterator *iter = xkb_compose_table_iterator_new(compose_table);
struct xkb_compose_table_entry *entry;
while ((entry = xkb_compose_table_iterator_next(iter))) {
print_compose_table_entry(entry);
}
xkb_compose_table_iterator_free(iter);
out: out:
xkb_compose_table_unref(compose_table); xkb_compose_table_unref(compose_table);
file_error:
xkb_context_unref(ctx); xkb_context_unref(ctx);
return ret; return ret;

View File

@ -109,3 +109,13 @@ global:
xkb_utf32_to_keysym; xkb_utf32_to_keysym;
xkb_keymap_key_get_mods_for_level; xkb_keymap_key_get_mods_for_level;
} V_0.8.0; } V_0.8.0;
V_1.6.0 {
global:
xkb_compose_table_entry_sequence;
xkb_compose_table_entry_keysym;
xkb_compose_table_entry_utf8;
xkb_compose_table_iterator_new;
xkb_compose_table_iterator_free;
xkb_compose_table_iterator_next;
} V_1.0.0;