diff --git a/Makefile.am b/Makefile.am index 0cd357a..f7e7fb1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -124,6 +124,7 @@ TESTS = \ test/context \ test/rules-file \ test/stringcomp \ + test/buffercomp \ test/log TESTS_LDADD = libtest.la @@ -134,6 +135,7 @@ test_context_LDADD = $(TESTS_LDADD) test_rules_file_CFLAGS = $(AM_CFLAGS) -Wno-declaration-after-statement test_rules_file_LDADD = $(TESTS_LDADD) -lrt test_stringcomp_LDADD = $(TESTS_LDADD) +test_buffercomp_LDADD = $(TESTS_LDADD) test_log_LDADD = $(TESTS_LDADD) test_rmlvo_to_kccgst_LDADD = $(TESTS_LDADD) test_print_compiled_keymap_LDADD = $(TESTS_LDADD) diff --git a/src/keymap.c b/src/keymap.c index b468f94..3df183a 100644 --- a/src/keymap.c +++ b/src/keymap.c @@ -221,6 +221,43 @@ xkb_keymap_new_from_string(struct xkb_context *ctx, return keymap; } +XKB_EXPORT struct xkb_keymap * +xkb_keymap_new_from_buffer(struct xkb_context *ctx, + const char *buffer, size_t length, + enum xkb_keymap_format format, + enum xkb_keymap_compile_flags flags) +{ + struct xkb_keymap *keymap; + const struct xkb_keymap_format_ops *ops; + + ops = get_keymap_format_ops(format); + if (!ops || !ops->keymap_new_from_string) { + log_err_func(ctx, "unsupported keymap format: %d\n", format); + return NULL; + } + + if (flags & ~(XKB_MAP_COMPILE_PLACEHOLDER)) { + log_err_func(ctx, "unrecognized flags: %#x\n", flags); + return NULL; + } + + if (!buffer) { + log_err_func1(ctx, "no buffer specified\n"); + return NULL; + } + + keymap = xkb_keymap_new(ctx, format, flags); + if (!keymap) + return NULL; + + if (!ops->keymap_new_from_buffer(keymap, buffer, length)) { + xkb_keymap_unref(keymap); + return NULL; + } + + return keymap; +} + XKB_EXPORT struct xkb_keymap * xkb_keymap_new_from_file(struct xkb_context *ctx, FILE *file, diff --git a/src/keymap.h b/src/keymap.h index 12a2771..c6b884d 100644 --- a/src/keymap.h +++ b/src/keymap.h @@ -440,6 +440,8 @@ struct xkb_keymap_format_ops { const struct xkb_rule_names *names); bool (*keymap_new_from_string)(struct xkb_keymap *keymap, const char *string); + bool (*keymap_new_from_buffer)(struct xkb_keymap *keymap, + const char *buffer, size_t length); bool (*keymap_new_from_file)(struct xkb_keymap *keymap, FILE *file); char *(*keymap_get_as_string)(struct xkb_keymap *keymap); }; diff --git a/src/xkbcomp/scanner.l b/src/xkbcomp/scanner.l index 5ccf3e9..f30462d 100644 --- a/src/xkbcomp/scanner.l +++ b/src/xkbcomp/scanner.l @@ -267,6 +267,38 @@ XkbParseString(struct xkb_context *ctx, const char *string, return xkb_file; } +/* + * yy_scan_buffer() requires the last two bytes of \buf to be 0. These two bytes + * are not scanned. Other zero bytes in the buffer are scanned normally, though. + * Due to these terminating zeroes, \length must be greater than 2. + * Furthermore, the buffer must be writable and you cannot make any assumptions + * about it after the scanner finished. + * All this must be guaranteed by the caller of this function! + */ +XkbFile * +XkbParseBuffer(struct xkb_context *ctx, char *buf, size_t length, + const char *file_name) +{ + yyscan_t scanner; + struct scanner_extra extra; + YY_BUFFER_STATE state; + XkbFile *xkb_file; + + if (!init_scanner(&scanner, &extra, ctx, file_name)) + return NULL; + + xkb_file = NULL; + state = yy_scan_buffer(buf, length, scanner); + if (state) { + xkb_file = parse(ctx, scanner, NULL); + yy_delete_buffer(state, scanner); + } + + clear_scanner(scanner); + + return xkb_file; +} + XkbFile * XkbParseFile(struct xkb_context *ctx, FILE *file, const char *file_name, const char *map) diff --git a/src/xkbcomp/xkbcomp-priv.h b/src/xkbcomp/xkbcomp-priv.h index 51c033f..4d421b5 100644 --- a/src/xkbcomp/xkbcomp-priv.h +++ b/src/xkbcomp/xkbcomp-priv.h @@ -48,6 +48,10 @@ XkbFile * XkbParseString(struct xkb_context *ctx, const char *string, const char *file_name); +XkbFile * +XkbParseBuffer(struct xkb_context *ctx, char *buf, size_t length, + const char *file_name); + void FreeXkbFile(XkbFile *file); diff --git a/src/xkbcomp/xkbcomp.c b/src/xkbcomp/xkbcomp.c index cc4b3ef..b9a1b5f 100644 --- a/src/xkbcomp/xkbcomp.c +++ b/src/xkbcomp/xkbcomp.c @@ -113,6 +113,38 @@ text_v1_keymap_new_from_string(struct xkb_keymap *keymap, const char *string) return ok; } +static bool +text_v1_keymap_new_from_buffer(struct xkb_keymap *keymap, + const char *buffer, size_t length) +{ + bool ok; + XkbFile *xkb_file; + char *buf; + + buf = malloc(length + 2); + if (!buf) { + log_err(keymap->ctx, "Cannot allocate memory for keymap\n"); + return NULL; + } + + /* yy_scan_buffer requires two terminating zero bytes */ + memcpy(buf, buffer, length); + buf[length] = 0; + buf[length + 1] = 0; + + xkb_file = XkbParseBuffer(keymap->ctx, buf, length + 2, "input"); + if (!xkb_file) { + log_err(keymap->ctx, "Failed to parse input xkb file\n"); + free(buf); + return NULL; + } + + ok = compile_keymap_file(keymap, xkb_file); + FreeXkbFile(xkb_file); + free(buf); + return ok; +} + static bool text_v1_keymap_new_from_file(struct xkb_keymap *keymap, FILE *file) { @@ -133,6 +165,7 @@ text_v1_keymap_new_from_file(struct xkb_keymap *keymap, FILE *file) const struct xkb_keymap_format_ops text_v1_keymap_format_ops = { .keymap_new_from_names = text_v1_keymap_new_from_names, .keymap_new_from_string = text_v1_keymap_new_from_string, + .keymap_new_from_buffer = text_v1_keymap_new_from_buffer, .keymap_new_from_file = text_v1_keymap_new_from_file, .keymap_get_as_string = text_v1_keymap_get_as_string, }; diff --git a/test/.gitignore b/test/.gitignore index 5a0b89f..d4bb051 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -7,6 +7,7 @@ state context rules-file stringcomp +buffercomp keyseq log interactive diff --git a/test/buffercomp.c b/test/buffercomp.c new file mode 100644 index 0000000..5cc1dbc --- /dev/null +++ b/test/buffercomp.c @@ -0,0 +1,90 @@ +/* + * Copyright © 2009 Dan Nicholson + * + * 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 +#include +#include + +#include "test.h" + +#define DATA_PATH "keymaps/stringcomp.data" + +int +main(int argc, char *argv[]) +{ + struct xkb_context *ctx = test_get_context(0); + struct xkb_keymap *keymap; + char *original, *dump; + + assert(ctx); + + /* Load in a prebuilt keymap, make sure we can compile it from memory, + * then compare it to make sure we get the same result when dumping it + * to a string. */ + original = test_read_file(DATA_PATH); + assert(original); + + keymap = test_compile_buffer(ctx, original, strlen(original)); + assert(keymap); + + dump = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_USE_ORIGINAL_FORMAT); + assert(dump); + + if (!streq(original, dump)) { + fprintf(stderr, + "round-trip test failed: dumped map differs from original\n"); + fprintf(stderr, "path to original file: %s\n", + test_get_path(DATA_PATH)); + fprintf(stderr, "length: dumped %lu, original %lu\n", + (unsigned long) strlen(dump), + (unsigned long) strlen(original)); + fprintf(stderr, "dumped map:\n"); + fprintf(stderr, "%s\n", dump); + fflush(stderr); + assert(0); + } + + free(original); + free(dump); + xkb_keymap_unref(keymap); + + /* Make sure we can't (falsely claim to) compile an empty string. */ + keymap = test_compile_buffer(ctx, "", 0); + assert(!keymap); + + /* Make sure we can recompile our output for a normal keymap from rules. */ + keymap = test_compile_rules(ctx, NULL, NULL, + "ru,ca,de,us", ",multix,neo,intl", NULL); + assert(keymap); + dump = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_USE_ORIGINAL_FORMAT); + assert(dump); + xkb_keymap_unref(keymap); + keymap = test_compile_buffer(ctx, dump, strlen(dump)); + assert(keymap); + xkb_keymap_unref(keymap); + free(dump); + + xkb_context_unref(ctx); + + return 0; +} diff --git a/test/common.c b/test/common.c index 6d6f25e..7b4ee00 100644 --- a/test/common.c +++ b/test/common.c @@ -260,6 +260,21 @@ test_compile_string(struct xkb_context *context, const char *string) return keymap; } +struct xkb_keymap * +test_compile_buffer(struct xkb_context *context, const char *buf, size_t len) +{ + struct xkb_keymap *keymap; + + keymap = xkb_keymap_new_from_buffer(context, buf, len, + XKB_KEYMAP_FORMAT_TEXT_V1, 0); + if (!keymap) { + fprintf(stderr, "Failed to compile keymap from memory buffer\n"); + return NULL; + } + + return keymap; +} + struct xkb_keymap * test_compile_rules(struct xkb_context *context, const char *rules, const char *model, const char *layout, diff --git a/test/test.h b/test/test.h index c39ef8d..804606e 100644 --- a/test/test.h +++ b/test/test.h @@ -69,6 +69,9 @@ test_compile_file(struct xkb_context *context, const char *path_rel); struct xkb_keymap * test_compile_string(struct xkb_context *context, const char *string); +struct xkb_keymap * +test_compile_buffer(struct xkb_context *context, const char *buf, size_t len); + struct xkb_keymap * test_compile_rules(struct xkb_context *context, const char *rules, const char *model, const char *layout, const char *variant, diff --git a/xkbcommon/xkbcommon.h b/xkbcommon/xkbcommon.h index ad758b8..a2aecfb 100644 --- a/xkbcommon/xkbcommon.h +++ b/xkbcommon/xkbcommon.h @@ -740,6 +740,20 @@ xkb_keymap_new_from_string(struct xkb_context *context, const char *string, enum xkb_keymap_format format, enum xkb_keymap_compile_flags flags); +/** + * Create a keymap from a memory buffer. + * + * This is just like xkb_keymap_new_from_string(), but takes a length argument + * so the input string does not have to be zero-terminated. + * + * @see xkb_keymap_new_from_string() + * @memberof xkb_keymap + */ +struct xkb_keymap * +xkb_keymap_new_from_buffer(struct xkb_context *context, const char *buffer, + size_t length, enum xkb_keymap_format format, + enum xkb_keymap_compile_flags flags); + /** * Take a new reference on a keymap. *