Skip to content

Commit

Permalink
rules: Resolve relative include statements using XKB paths
Browse files Browse the repository at this point in the history
Contrary to keymap files, the `! include` statement in rules does not
lookup include paths added to `xkb_context`. So it is not possible e.g.
to import another file in the same folder without using an absolute path.

- Added path utils: `is_absolute(path)`.
- Added XKB paths lookup to enable e.g. `! include evdev` to work.
- Added test.
  • Loading branch information
wismill committed Sep 16, 2024
1 parent 05ba96d commit 006ba4e
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 14 deletions.
1 change: 1 addition & 0 deletions changes/api/501.rules-includes.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rules: Use XKB paths to resolve relative paths in include statements.
4 changes: 3 additions & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,11 @@ libxkbcommon_sources = [
'src/text.h',
'src/utf8.c',
'src/utf8.h',
'src/util-mem.h',
'src/utils.c',
'src/utils.h',
'src/util-mem.h',
'src/utils-paths.c',
'src/utils-paths.h',
]
libxkbcommon_link_args = []
libxkbcommon_link_deps = []
Expand Down
50 changes: 50 additions & 0 deletions src/utils-paths.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright © 2024 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 "utils.h"
#include "utils-paths.h"


bool
is_absolute(const char *path)
{
const size_t len = strlen_safe(path);
#ifdef _WIN32
/*
* A file name is relative to the current directory if it does not begin with
* one of the following:
* - A UNC name of any format, which always start with two backslash characters ("\\").
* - A disk designator with a backslash, for example "C:\" or "d:\".
* - A single backslash, for example, "\directory" or "\file.txt".
* See: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
*/
return len >= 1 &&
((path[0] == PATH_SEPARATOR || path[0] == ALT_PATH_SEPARATOR) ||
(len >= 3 && path[1] == ':' &&
(path[2] == PATH_SEPARATOR || path[2] == ALT_PATH_SEPARATOR)));
#else
return len >= 1 && path[0] == PATH_SEPARATOR;
#endif
}
16 changes: 16 additions & 0 deletions src/utils-paths.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef UTILS_PATHS_H
#define UTILS_PATHS_H

#include <stdbool.h>

#ifdef _WIN32
#define PATH_SEPARATOR '\\'
#define ALT_PATH_SEPARATOR '/'
#else
#define PATH_SEPARATOR '/'
#endif

bool
is_absolute(const char *path);

#endif
34 changes: 25 additions & 9 deletions src/xkbcomp/rules.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
#include "rules.h"
#include "include.h"
#include "scanner-utils.h"
#include "utils-paths.h"

#define MAX_INCLUDE_DEPTH 5

Expand Down Expand Up @@ -369,7 +370,6 @@ matcher_include(struct matcher *m, struct scanner *parent_scanner,
struct sval inc)
{
struct scanner s; /* parses the !include value */
FILE *file;

scanner_init(&s, m->ctx, inc.start, inc.len,
parent_scanner->file_name, NULL);
Expand All @@ -383,6 +383,7 @@ matcher_include(struct matcher *m, struct scanner *parent_scanner,
return;
}

/* Proceed to %-expansion */
while (!scanner_eof(&s) && !scanner_eol(&s)) {
if (scanner_chr(&s, '%')) {
if (scanner_chr(&s, '%')) {
Expand Down Expand Up @@ -427,19 +428,34 @@ matcher_include(struct matcher *m, struct scanner *parent_scanner,
return;
}

file = fopen(s.buf, "rb");
if (file) {
/* Lookup rules file in XKB paths only if the include path is relative */
unsigned int offset = 0;
FILE *file;
bool absolute_path = is_absolute(s.buf);
if (absolute_path)
file = fopen(s.buf, "rb");
else
file = FindFileInXkbPath(m->ctx, s.buf, FILE_TYPE_RULES, NULL, &offset);

while (file) {
bool ret = read_rules_file(m->ctx, m, include_depth + 1, file, s.buf);
if (!ret)
log_err(m->ctx, XKB_LOG_MESSAGE_NO_ID,
"No components returned from included XKB rules \"%s\"\n",
s.buf);
fclose(file);
} else {
if (ret)
return;
/* Failed to parse rules or get all the components */
log_err(m->ctx, XKB_LOG_MESSAGE_NO_ID,
"Failed to open included XKB rules \"%s\"\n",
"No components returned from included XKB rules \"%s\"\n",
s.buf);
if (absolute_path)
break;
/* Try next XKB path */
offset++;
file = FindFileInXkbPath(m->ctx, s.buf, FILE_TYPE_RULES, NULL, &offset);
}

log_err(m->ctx, XKB_LOG_MESSAGE_NO_ID,
"Failed to open included XKB rules \"%s\"\n",
s.buf);
}

static void
Expand Down
4 changes: 4 additions & 0 deletions test/data/rules/inc-src-relative-path
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
! layout = symbols
my_layout = my_symbols

! include inc-dst-simple
10 changes: 10 additions & 0 deletions test/rules-file-includes.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,16 @@ main(int argc, char *argv[])
};
assert(test_rules(ctx, &test7));

struct test_data test8 = {
.rules = "inc-src-relative-path",

.model = "my_model", .layout = "my_layout", .variant = "", .options = "",

.keycodes = "my_keycodes", .types = "default_types",
.compat = "default_compat", .symbols = "my_symbols",
};
assert(test_rules(ctx, &test8));

xkb_context_unref(ctx);
return 0;
}
38 changes: 34 additions & 4 deletions test/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,13 @@

#include "test.h"
#include "utils.h"
#include "utils-paths.h"

int
main(void)
static void
test_string_functions(void)
{
char buffer[10];

test_init();

assert(!snprintf_safe(buffer, 0, "foo"));
assert(!snprintf_safe(buffer, 1, "foo"));
assert(!snprintf_safe(buffer, 3, "foo"));
Expand All @@ -53,6 +52,37 @@ main(void)
assert(!streq_null("foobar", NULL));
assert(!streq_null(NULL, "foobar"));
assert(streq_null(NULL, NULL));
}

static void
test_path_functions(void)
{
/* Absolute paths */
#ifdef _WIN32
assert(!is_absolute("path\\test"));
assert(is_absolute("c:\\test"));
assert(!is_absolute("c:test"));
assert(is_absolute("c:\\"));
assert(is_absolute("c:/"));
assert(!is_absolute("c:"));
assert(is_absolute("\\\\foo"));
assert(is_absolute("\\\\?\\foo"));
assert(is_absolute("\\\\?\\UNC\\foo"));
assert(is_absolute("/foo"));
assert(is_absolute("\\foo"));
#else
assert(!is_absolute("test/path"));
assert(is_absolute("/test" ));
assert(is_absolute("/" ));
#endif
}

int
main(void)
{
test_init();
test_string_functions();
test_path_functions();

return 0;
}

0 comments on commit 006ba4e

Please sign in to comment.