324 lines
10 KiB
C
324 lines
10 KiB
C
/************************************************************
|
|
* Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this
|
|
* software and its documentation for any purpose and without
|
|
* fee is hereby granted, provided that the above copyright
|
|
* notice appear in all copies and that both that copyright
|
|
* notice and this permission notice appear in supporting
|
|
* documentation, and that the name of Silicon Graphics not be
|
|
* used in advertising or publicity pertaining to distribution
|
|
* of the software without specific prior written permission.
|
|
* Silicon Graphics makes no representation about the suitability
|
|
* of this software for any purpose. It is provided "as is"
|
|
* without any express or implied warranty.
|
|
*
|
|
* SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
|
|
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
|
|
* GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
|
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
|
|
* THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
********************************************************/
|
|
|
|
/*
|
|
* Copyright © 2012 Ran Benita <ran234@gmail.com>
|
|
*
|
|
* 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 <errno.h>
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
|
|
#include "xkbcomp-priv.h"
|
|
#include "include.h"
|
|
|
|
/**
|
|
* Parse an include statement. Each call returns a file name, along with
|
|
* (possibly) a specific map in the file, an explicit group designator, and
|
|
* the separator from the next file, used to determine the merge mode.
|
|
*
|
|
* @param str_inout Input statement, modified in-place. Should be passed in
|
|
* repeatedly. If str_inout is NULL, the parsing has completed.
|
|
*
|
|
* @param file_rtrn Set to the name of the include file to be used. Combined
|
|
* with an enum xkb_file_type, this determines which file to look for in the
|
|
* include path.
|
|
*
|
|
* @param map_rtrn Set to the string between '(' and ')', if any. This will
|
|
* result in the compilation of a specific named map within the file (e.g.
|
|
* xkb_symbols "basic" { ... }) , as opposed to the default map of the file.
|
|
*
|
|
* @param nextop_rtrn Set to the next operation in the complete statement,
|
|
* which is '\0' if it's the last file or '+' or '|' if there are more.
|
|
* Separating the files with '+' sets the merge mode to MERGE_MODE_OVERRIDE,
|
|
* while '|' sets the merge mode to MERGE_MODE_AUGMENT.
|
|
*
|
|
* @param extra_data Set to the string after ':', if any. Currently the
|
|
* extra data is only used for setting an explicit group index for a symbols
|
|
* file.
|
|
*
|
|
* @return true if parsing was successful, false for an illegal string.
|
|
*
|
|
* Example: "evdev+aliases(qwerty):2"
|
|
* str_inout = "aliases(qwerty):2"
|
|
* file_rtrn = "evdev"
|
|
* map_rtrn = NULL
|
|
* nextop_retrn = "+"
|
|
* extra_data = NULL
|
|
*
|
|
* 2nd run with "aliases(qwerty):2"
|
|
* str_inout = NULL
|
|
* file_rtrn = "aliases"
|
|
* map_rtrn = "qwerty"
|
|
* nextop_retrn = ""
|
|
* extra_data = "2"
|
|
*
|
|
*/
|
|
bool
|
|
ParseIncludeMap(char **str_inout, char **file_rtrn, char **map_rtrn,
|
|
char *nextop_rtrn, char **extra_data)
|
|
{
|
|
char *tmp, *str, *next;
|
|
|
|
str = *str_inout;
|
|
|
|
/*
|
|
* Find the position in the string where the next file is included,
|
|
* if there is more than one left in the statement.
|
|
*/
|
|
next = strpbrk(str, "|+");
|
|
if (next) {
|
|
/* Got more files, this function will be called again. */
|
|
*nextop_rtrn = *next;
|
|
/* Separate the string, for strchr etc. to work on this file only. */
|
|
*next++ = '\0';
|
|
}
|
|
else {
|
|
/* This is the last file in this statement, won't be called again. */
|
|
*nextop_rtrn = '\0';
|
|
next = NULL;
|
|
}
|
|
|
|
/*
|
|
* Search for the explicit group designator, if any. If it's there,
|
|
* it goes after the file name and map.
|
|
*/
|
|
tmp = strchr(str, ':');
|
|
if (tmp != NULL) {
|
|
*tmp++ = '\0';
|
|
*extra_data = strdup(tmp);
|
|
}
|
|
else {
|
|
*extra_data = NULL;
|
|
}
|
|
|
|
/* Look for a map, if any. */
|
|
tmp = strchr(str, '(');
|
|
if (tmp == NULL) {
|
|
/* No map. */
|
|
*file_rtrn = strdup(str);
|
|
*map_rtrn = NULL;
|
|
}
|
|
else if (str[0] == '(') {
|
|
/* Map without file - invalid. */
|
|
free(*extra_data);
|
|
return false;
|
|
}
|
|
else {
|
|
/* Got a map; separate the file and the map for the strdup's. */
|
|
*tmp++ = '\0';
|
|
*file_rtrn = strdup(str);
|
|
str = tmp;
|
|
tmp = strchr(str, ')');
|
|
if (tmp == NULL || tmp[1] != '\0') {
|
|
free(*file_rtrn);
|
|
free(*extra_data);
|
|
return false;
|
|
}
|
|
*tmp++ = '\0';
|
|
*map_rtrn = strdup(str);
|
|
}
|
|
|
|
/* Set up the next file for the next call, if any. */
|
|
if (*nextop_rtrn == '\0')
|
|
*str_inout = NULL;
|
|
else if (*nextop_rtrn == '|' || *nextop_rtrn == '+')
|
|
*str_inout = next;
|
|
else
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static const char *xkb_file_type_include_dirs[_FILE_TYPE_NUM_ENTRIES] = {
|
|
[FILE_TYPE_KEYCODES] = "keycodes",
|
|
[FILE_TYPE_TYPES] = "types",
|
|
[FILE_TYPE_COMPAT] = "compat",
|
|
[FILE_TYPE_SYMBOLS] = "symbols",
|
|
[FILE_TYPE_GEOMETRY] = "geometry",
|
|
[FILE_TYPE_KEYMAP] = "keymap",
|
|
[FILE_TYPE_RULES] = "rules",
|
|
};
|
|
|
|
/**
|
|
* Return the xkb directory based on the type.
|
|
*/
|
|
static const char *
|
|
DirectoryForInclude(enum xkb_file_type type)
|
|
{
|
|
if (type >= _FILE_TYPE_NUM_ENTRIES)
|
|
return "";
|
|
return xkb_file_type_include_dirs[type];
|
|
}
|
|
|
|
static void
|
|
LogIncludePaths(struct xkb_context *ctx)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (xkb_context_num_include_paths(ctx) > 0) {
|
|
log_err(ctx, "%d include paths searched:\n",
|
|
xkb_context_num_include_paths(ctx));
|
|
for (i = 0; i < xkb_context_num_include_paths(ctx); i++)
|
|
log_err(ctx, "\t%s\n",
|
|
xkb_context_include_path_get(ctx, i));
|
|
}
|
|
else {
|
|
log_err(ctx, "There are no include paths to search\n");
|
|
}
|
|
|
|
if (xkb_context_num_failed_include_paths(ctx) > 0) {
|
|
log_err(ctx, "%d include paths could not be added:\n",
|
|
xkb_context_num_failed_include_paths(ctx));
|
|
for (i = 0; i < xkb_context_num_failed_include_paths(ctx); i++)
|
|
log_err(ctx, "\t%s\n",
|
|
xkb_context_failed_include_path_get(ctx, i));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return an open file handle to the first file (counting from offset) with the
|
|
* given name in the include paths, starting at the offset.
|
|
*
|
|
* offset must be zero the first time this is called and is set to the index the
|
|
* file was found. Call again with offset+1 to keep searching through the
|
|
* include paths.
|
|
*
|
|
* If this function returns NULL, no more files are available.
|
|
*/
|
|
FILE *
|
|
FindFileInXkbPath(struct xkb_context *ctx, const char *name,
|
|
enum xkb_file_type type, char **pathRtrn,
|
|
unsigned int *offset)
|
|
{
|
|
unsigned int i;
|
|
FILE *file = NULL;
|
|
char *buf = NULL;
|
|
const char *typeDir;
|
|
|
|
typeDir = DirectoryForInclude(type);
|
|
|
|
for (i = *offset; i < xkb_context_num_include_paths(ctx); i++) {
|
|
buf = asprintf_safe("%s/%s/%s", xkb_context_include_path_get(ctx, i),
|
|
typeDir, name);
|
|
if (!buf) {
|
|
log_err(ctx, "Failed to alloc buffer for (%s/%s/%s)\n",
|
|
xkb_context_include_path_get(ctx, i), typeDir, name);
|
|
continue;
|
|
}
|
|
|
|
file = fopen(buf, "rb");
|
|
if (file) {
|
|
if (pathRtrn) {
|
|
*pathRtrn = buf;
|
|
buf = NULL;
|
|
}
|
|
*offset = i;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* We only print warnings if we can't find the file on the first lookup */
|
|
if (*offset == 0) {
|
|
log_err(ctx, "Couldn't find file \"%s/%s\" in include paths\n",
|
|
typeDir, name);
|
|
LogIncludePaths(ctx);
|
|
}
|
|
|
|
out:
|
|
free(buf);
|
|
return file;
|
|
}
|
|
|
|
XkbFile *
|
|
ProcessIncludeFile(struct xkb_context *ctx, IncludeStmt *stmt,
|
|
enum xkb_file_type file_type)
|
|
{
|
|
FILE *file;
|
|
XkbFile *xkb_file = NULL;
|
|
unsigned int offset = 0;
|
|
|
|
file = FindFileInXkbPath(ctx, stmt->file, file_type, NULL, &offset);
|
|
if (!file)
|
|
return NULL;
|
|
|
|
while (file) {
|
|
xkb_file = XkbParseFile(ctx, file, stmt->file, stmt->map);
|
|
fclose(file);
|
|
|
|
if (xkb_file) {
|
|
if (xkb_file->file_type != file_type) {
|
|
log_err(ctx,
|
|
"Include file of wrong type (expected %s, got %s); "
|
|
"Include file \"%s\" ignored\n",
|
|
xkb_file_type_to_string(file_type),
|
|
xkb_file_type_to_string(xkb_file->file_type), stmt->file);
|
|
FreeXkbFile(xkb_file);
|
|
xkb_file = NULL;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
offset++;
|
|
file = FindFileInXkbPath(ctx, stmt->file, file_type, NULL, &offset);
|
|
}
|
|
|
|
if (!xkb_file) {
|
|
if (stmt->map)
|
|
log_err(ctx, "Couldn't process include statement for '%s(%s)'\n",
|
|
stmt->file, stmt->map);
|
|
else
|
|
log_err(ctx, "Couldn't process include statement for '%s'\n",
|
|
stmt->file);
|
|
}
|
|
|
|
/* FIXME: we have to check recursive includes here (or somewhere) */
|
|
|
|
return xkb_file;
|
|
}
|