1 /************************************************************ 2 * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc. 3 * 4 * Permission to use, copy, modify, and distribute this 5 * software and its documentation for any purpose and without 6 * fee is hereby granted, provided that the above copyright 7 * notice appear in all copies and that both that copyright 8 * notice and this permission notice appear in supporting 9 * documentation, and that the name of Silicon Graphics not be 10 * used in advertising or publicity pertaining to distribution 11 * of the software without specific prior written permission. 12 * Silicon Graphics makes no representation about the suitability 13 * of this software for any purpose. It is provided "as is" 14 * without any express or implied warranty. 15 * 16 * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 17 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 18 * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON 19 * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 20 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 21 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 22 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH 23 * THE USE OR PERFORMANCE OF THIS SOFTWARE. 24 * 25 ********************************************************/ 26 27 /* 28 * Copyright © 2012 Ran Benita <ran234@gmail.com> 29 * 30 * Permission is hereby granted, free of charge, to any person obtaining a 31 * copy of this software and associated documentation files (the "Software"), 32 * to deal in the Software without restriction, including without limitation 33 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 34 * and/or sell copies of the Software, and to permit persons to whom the 35 * Software is furnished to do so, subject to the following conditions: 36 * 37 * The above copyright notice and this permission notice (including the next 38 * paragraph) shall be included in all copies or substantial portions of the 39 * Software. 40 * 41 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 42 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 43 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 44 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 45 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 46 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 47 * DEALINGS IN THE SOFTWARE. 48 */ 49 50 #include <errno.h> 51 #include <limits.h> 52 #include <stdio.h> 53 54 #include "xkbcomp-priv.h" 55 #include "include.h" 56 57 /** 58 * Parse an include statement. Each call returns a file name, along with 59 * (possibly) a specific map in the file, an explicit group designator, and 60 * the separator from the next file, used to determine the merge mode. 61 * 62 * @param str_inout Input statement, modified in-place. Should be passed in 63 * repeatedly. If str_inout is NULL, the parsing has completed. 64 * 65 * @param file_rtrn Set to the name of the include file to be used. Combined 66 * with an enum xkb_file_type, this determines which file to look for in the 67 * include path. 68 * 69 * @param map_rtrn Set to the string between '(' and ')', if any. This will 70 * result in the compilation of a specific named map within the file (e.g. 71 * xkb_symbols "basic" { ... }) , as opposed to the default map of the file. 72 * 73 * @param nextop_rtrn Set to the next operation in the complete statement, 74 * which is '\0' if it's the last file or '+' or '|' if there are more. 75 * Separating the files with '+' sets the merge mode to MERGE_MODE_OVERRIDE, 76 * while '|' sets the merge mode to MERGE_MODE_AUGMENT. 77 * 78 * @param extra_data Set to the string after ':', if any. Currently the 79 * extra data is only used for setting an explicit group index for a symbols 80 * file. 81 * 82 * @return true if parsing was successful, false for an illegal string. 83 * 84 * Example: "evdev+aliases(qwerty):2" 85 * str_inout = "aliases(qwerty):2" 86 * file_rtrn = "evdev" 87 * map_rtrn = NULL 88 * nextop_retrn = "+" 89 * extra_data = NULL 90 * 91 * 2nd run with "aliases(qwerty):2" 92 * str_inout = NULL 93 * file_rtrn = "aliases" 94 * map_rtrn = "qwerty" 95 * nextop_retrn = "" 96 * extra_data = "2" 97 * 98 */ 99 bool 100 ParseIncludeMap(char **str_inout, char **file_rtrn, char **map_rtrn, 101 char *nextop_rtrn, char **extra_data) 102 { 103 char *tmp, *str, *next; 104 105 str = *str_inout; 106 107 /* 108 * Find the position in the string where the next file is included, 109 * if there is more than one left in the statement. 110 */ 111 next = strpbrk(str, "|+"); 112 if (next) { 113 /* Got more files, this function will be called again. */ 114 *nextop_rtrn = *next; 115 /* Separate the string, for strchr etc. to work on this file only. */ 116 *next++ = '\0'; 117 } 118 else { 119 /* This is the last file in this statement, won't be called again. */ 120 *nextop_rtrn = '\0'; 121 next = NULL; 122 } 123 124 /* 125 * Search for the explicit group designator, if any. If it's there, 126 * it goes after the file name and map. 127 */ 128 tmp = strchr(str, ':'); 129 if (tmp != NULL) { 130 *tmp++ = '\0'; 131 *extra_data = strdup(tmp); 132 } 133 else { 134 *extra_data = NULL; 135 } 136 137 /* Look for a map, if any. */ 138 tmp = strchr(str, '('); 139 if (tmp == NULL) { 140 /* No map. */ 141 *file_rtrn = strdup(str); 142 *map_rtrn = NULL; 143 } 144 else if (str[0] == '(') { 145 /* Map without file - invalid. */ 146 free(*extra_data); 147 return false; 148 } 149 else { 150 /* Got a map; separate the file and the map for the strdup's. */ 151 *tmp++ = '\0'; 152 *file_rtrn = strdup(str); 153 str = tmp; 154 tmp = strchr(str, ')'); 155 if (tmp == NULL || tmp[1] != '\0') { 156 free(*file_rtrn); 157 free(*extra_data); 158 return false; 159 } 160 *tmp++ = '\0'; 161 *map_rtrn = strdup(str); 162 } 163 164 /* Set up the next file for the next call, if any. */ 165 if (*nextop_rtrn == '\0') 166 *str_inout = NULL; 167 else if (*nextop_rtrn == '|' || *nextop_rtrn == '+') 168 *str_inout = next; 169 else 170 return false; 171 172 return true; 173 } 174 175 static const char *xkb_file_type_include_dirs[_FILE_TYPE_NUM_ENTRIES] = { 176 [FILE_TYPE_KEYCODES] = "keycodes", 177 [FILE_TYPE_TYPES] = "types", 178 [FILE_TYPE_COMPAT] = "compat", 179 [FILE_TYPE_SYMBOLS] = "symbols", 180 [FILE_TYPE_GEOMETRY] = "geometry", 181 [FILE_TYPE_KEYMAP] = "keymap", 182 [FILE_TYPE_RULES] = "rules", 183 }; 184 185 /** 186 * Return the xkb directory based on the type. 187 */ 188 static const char * 189 DirectoryForInclude(enum xkb_file_type type) 190 { 191 if (type >= _FILE_TYPE_NUM_ENTRIES) 192 return ""; 193 return xkb_file_type_include_dirs[type]; 194 } 195 196 FILE * 197 FindFileInXkbPath(struct xkb_context *ctx, const char *name, 198 enum xkb_file_type type, char **pathRtrn) 199 { 200 unsigned int i; 201 FILE *file = NULL; 202 char *buf = NULL; 203 const char *typeDir; 204 size_t buf_size = 0, typeDirLen, name_len; 205 206 typeDir = DirectoryForInclude(type); 207 typeDirLen = strlen(typeDir); 208 name_len = strlen(name); 209 210 for (i = 0; i < xkb_context_num_include_paths(ctx); i++) { 211 size_t new_buf_size = strlen(xkb_context_include_path_get(ctx, i)) + 212 typeDirLen + name_len + 3; 213 int ret; 214 if (new_buf_size > buf_size) { 215 void *buf_new = realloc(buf, new_buf_size); 216 if (buf_new) { 217 buf_size = new_buf_size; 218 buf = buf_new; 219 } else { 220 log_err(ctx, "Cannot realloc for name (%s/%s/%s)\n", 221 xkb_context_include_path_get(ctx, i), typeDir, name); 222 continue; 223 } 224 } 225 ret = snprintf(buf, buf_size, "%s/%s/%s", 226 xkb_context_include_path_get(ctx, i), 227 typeDir, name); 228 if (ret < 0) { 229 log_err(ctx, "snprintf error (%s/%s/%s)\n", 230 xkb_context_include_path_get(ctx, i), typeDir, name); 231 continue; 232 } 233 234 file = fopen(buf, "r"); 235 if (file) 236 break; 237 } 238 239 if (!file) { 240 log_err(ctx, "Couldn't find file \"%s/%s\" in include paths\n", 241 typeDir, name); 242 243 if (xkb_context_num_include_paths(ctx) > 0) { 244 log_err(ctx, "%d include paths searched:\n", 245 xkb_context_num_include_paths(ctx)); 246 for (i = 0; i < xkb_context_num_include_paths(ctx); i++) 247 log_err(ctx, "\t%s\n", 248 xkb_context_include_path_get(ctx, i)); 249 } 250 else { 251 log_err(ctx, "There are no include paths to search\n"); 252 } 253 254 if (xkb_context_num_failed_include_paths(ctx) > 0) { 255 log_err(ctx, "%d include paths could not be added:\n", 256 xkb_context_num_failed_include_paths(ctx)); 257 for (i = 0; i < xkb_context_num_failed_include_paths(ctx); i++) 258 log_err(ctx, "\t%s\n", 259 xkb_context_failed_include_path_get(ctx, i)); 260 } 261 262 free(buf); 263 return NULL; 264 } 265 266 if (pathRtrn) 267 *pathRtrn = buf; 268 else 269 free(buf); 270 return file; 271 } 272 273 XkbFile * 274 ProcessIncludeFile(struct xkb_context *ctx, IncludeStmt *stmt, 275 enum xkb_file_type file_type) 276 { 277 FILE *file; 278 XkbFile *xkb_file; 279 280 file = FindFileInXkbPath(ctx, stmt->file, file_type, NULL); 281 if (!file) 282 return false; 283 284 xkb_file = XkbParseFile(ctx, file, stmt->file, stmt->map); 285 fclose(file); 286 if (!xkb_file) { 287 if (stmt->map) 288 log_err(ctx, "Couldn't process include statement for '%s(%s)'\n", 289 stmt->file, stmt->map); 290 else 291 log_err(ctx, "Couldn't process include statement for '%s'\n", 292 stmt->file); 293 return NULL; 294 } 295 296 if (xkb_file->file_type != file_type) { 297 log_err(ctx, 298 "Include file of wrong type (expected %s, got %s); " 299 "Include file \"%s\" ignored\n", 300 xkb_file_type_to_string(file_type), 301 xkb_file_type_to_string(xkb_file->file_type), stmt->file); 302 FreeXkbFile(xkb_file); 303 return NULL; 304 } 305 306 /* FIXME: we have to check recursive includes here (or somewhere) */ 307 308 return xkb_file; 309 } 310