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#include <X11/Xlib.h> 28#include <X11/XKBlib.h> 29 30#define DEBUG_VAR debugFlags 31#include "utils.h" 32#include <stdlib.h> 33#include <X11/extensions/XKM.h> 34#include "xkbpath.h" 35 36#ifndef PATH_MAX 37#define PATH_MAX 1024 38#endif 39 40#define PATH_CHUNK 8 /* initial szPath */ 41 42static Bool noDefaultPath = False; 43static int szPath; /* number of entries allocated for includePath */ 44static int nPathEntries; /* number of actual entries in includePath */ 45static char **includePath; /* Holds all directories we might be including data from */ 46 47/** 48 * Extract the first token from an include statement. 49 * @param str_inout Input statement, modified in-place. Can be passed in 50 * repeatedly. If str_inout is NULL, the parsing has completed. 51 * @param file_rtrn Set to the include file to be used. 52 * @param map_rtrn Set to whatever comes after ), if any. 53 * @param nextop_rtrn Set to the next operation in the complete statement. 54 * @param extra_data Set to the string between ( and ), if any. 55 * 56 * @return True if parsing was successful, False for an illegal string. 57 * 58 * Example: "evdev+aliases(qwerty)" 59 * str_inout = aliases(qwerty) 60 * nextop_retrn = + 61 * extra_data = NULL 62 * file_rtrn = evdev 63 * map_rtrn = NULL 64 * 65 * 2nd run with "aliases(qwerty)" 66 * str_inout = NULL 67 * file_rtrn = aliases 68 * map_rtrn = qwerty 69 * extra_data = NULL 70 * nextop_retrn = "" 71 * 72 */ 73Bool 74XkbParseIncludeMap(char **str_inout, char **file_rtrn, char **map_rtrn, 75 char *nextop_rtrn, char **extra_data) 76{ 77 char *tmp, *str, *next; 78 79 str = *str_inout; 80 if ((*str == '+') || (*str == '|')) 81 { 82 *file_rtrn = *map_rtrn = NULL; 83 *nextop_rtrn = *str; 84 next = str + 1; 85 } 86 else if (*str == '%') 87 { 88 *file_rtrn = *map_rtrn = NULL; 89 *nextop_rtrn = str[1]; 90 next = str + 2; 91 } 92 else 93 { 94 /* search for tokens inside the string */ 95 next = strpbrk(str, "|+"); 96 if (next) 97 { 98 /* set nextop_rtrn to \0, next to next character */ 99 *nextop_rtrn = *next; 100 *next++ = '\0'; 101 } 102 else 103 { 104 *nextop_rtrn = '\0'; 105 next = NULL; 106 } 107 /* search for :, store result in extra_data */ 108 tmp = strchr(str, ':'); 109 if (tmp != NULL) 110 { 111 *tmp++ = '\0'; 112 *extra_data = uStringDup(tmp); 113 } 114 else 115 { 116 *extra_data = NULL; 117 } 118 tmp = strchr(str, '('); 119 if (tmp == NULL) 120 { 121 *file_rtrn = uStringDup(str); 122 *map_rtrn = NULL; 123 } 124 else if (str[0] == '(') 125 { 126 free(*extra_data); 127 return False; 128 } 129 else 130 { 131 *tmp++ = '\0'; 132 *file_rtrn = uStringDup(str); 133 str = tmp; 134 tmp = strchr(str, ')'); 135 if ((tmp == NULL) || (tmp[1] != '\0')) 136 { 137 free(*file_rtrn); 138 free(*extra_data); 139 return False; 140 } 141 *tmp++ = '\0'; 142 *map_rtrn = uStringDup(str); 143 } 144 } 145 if (*nextop_rtrn == '\0') 146 *str_inout = NULL; 147 else if ((*nextop_rtrn == '|') || (*nextop_rtrn == '+')) 148 *str_inout = next; 149 else 150 return False; 151 return True; 152} 153 154/** 155 * Init memory for include paths. 156 */ 157Bool 158XkbInitIncludePath(void) 159{ 160 szPath = PATH_CHUNK; 161 includePath = (char **) calloc(szPath, sizeof(char *)); 162 if (includePath == NULL) 163 return False; 164 return True; 165} 166 167void 168XkbAddDefaultDirectoriesToPath(void) 169{ 170 if (noDefaultPath) 171 return; 172 XkbAddDirectoryToPath(DFLT_XKB_CONFIG_ROOT); 173} 174 175/** 176 * Remove all entries from the global includePath. 177 */ 178static void 179XkbClearIncludePath(void) 180{ 181 if (szPath > 0) 182 { 183 for (int i = 0; i < nPathEntries; i++) 184 { 185 free(includePath[i]); 186 includePath[i] = NULL; 187 } 188 nPathEntries = 0; 189 } 190 noDefaultPath = True; 191 return; 192} 193 194/** 195 * Add the given path to the global includePath variable. 196 * If dir is NULL, the includePath is emptied. 197 */ 198Bool 199XkbAddDirectoryToPath(const char *dir) 200{ 201 int len; 202 if ((dir == NULL) || (dir[0] == '\0')) 203 { 204 XkbClearIncludePath(); 205 return True; 206 } 207 len = strlen(dir); 208 if (len + 2 >= PATH_MAX) 209 { /* allow for '/' and at least one character */ 210 ERROR("Path entry (%s) too long (maximum length is %d)\n", 211 dir, PATH_MAX - 3); 212 return False; 213 } 214 if (nPathEntries >= szPath) 215 { 216 char **new; 217 szPath += PATH_CHUNK; 218 new = (char **) realloc(includePath, szPath * sizeof(char *)); 219 if (new == NULL) 220 { 221 WSGO("Allocation failed (includePath)\n"); 222 return False; 223 } 224 else 225 includePath = new; 226 } 227 includePath[nPathEntries] = strdup(dir); 228 if (includePath[nPathEntries] == NULL) 229 { 230 WSGO("Allocation failed (includePath[%d])\n", nPathEntries); 231 return False; 232 } 233 nPathEntries++; 234 return True; 235} 236 237/***====================================================================***/ 238 239/** 240 * Return the xkb directory based on the type. 241 * Do not free the memory returned by this function. 242 */ 243char * 244XkbDirectoryForInclude(unsigned type) 245{ 246 static char buf[32]; 247 248 switch (type) 249 { 250 case XkmSemanticsFile: 251 strcpy(buf, "semantics"); 252 break; 253 case XkmLayoutFile: 254 strcpy(buf, "layout"); 255 break; 256 case XkmKeymapFile: 257 strcpy(buf, "keymap"); 258 break; 259 case XkmKeyNamesIndex: 260 strcpy(buf, "keycodes"); 261 break; 262 case XkmTypesIndex: 263 strcpy(buf, "types"); 264 break; 265 case XkmSymbolsIndex: 266 strcpy(buf, "symbols"); 267 break; 268 case XkmCompatMapIndex: 269 strcpy(buf, "compat"); 270 break; 271 case XkmGeometryFile: 272 case XkmGeometryIndex: 273 strcpy(buf, "geometry"); 274 break; 275 default: 276 strcpy(buf, ""); 277 break; 278 } 279 return buf; 280} 281 282/***====================================================================***/ 283 284typedef struct _FileCacheEntry 285{ 286 const char *name; 287 unsigned type; 288 char *path; 289 void *data; 290 struct _FileCacheEntry *next; 291} FileCacheEntry; 292static FileCacheEntry *fileCache; 293 294/** 295 * Add the file with the given name to the internal cache to avoid opening and 296 * parsing the file multiple times. If a cache entry for the same name + type 297 * is already present, the entry is overwritten and the data belonging to the 298 * previous entry is returned. 299 * 300 * @parameter name The name of the file (e.g. evdev). 301 * @parameter type Type of the file (XkbTypesIdx, ... or XkbSemanticsFile, ...) 302 * @parameter path The full path to the file. 303 * @parameter data Already parsed data. 304 * 305 * @return The data from the overwritten file or NULL. 306 */ 307void * 308XkbAddFileToCache(const char *name, unsigned type, char *path, void *data) 309{ 310 FileCacheEntry *entry; 311 312 for (entry = fileCache; entry != NULL; entry = entry->next) 313 { 314 if ((type == entry->type) && (uStringEqual(name, entry->name))) 315 { 316 void *old = entry->data; 317 WSGO("Replacing file cache entry (%s/%d)\n", name, type); 318 entry->path = path; 319 entry->data = data; 320 return old; 321 } 322 } 323 entry = malloc(sizeof(FileCacheEntry)); 324 if (entry != NULL) 325 { 326 *entry = (FileCacheEntry) { 327 .name = name, 328 .type = type, 329 .path = path, 330 .data = data, 331 .next = fileCache 332 }; 333 fileCache = entry; 334 } 335 return NULL; 336} 337 338/** 339 * Search for the given name + type in the cache. 340 * 341 * @parameter name The name of the file (e.g. evdev). 342 * @parameter type Type of the file (XkbTypesIdx, ... or XkbSemanticsFile, ...) 343 * @parameter pathRtrn Set to the full path of the given entry. 344 * 345 * @return the data from the cache entry or NULL if no matching entry was found. 346 */ 347void * 348XkbFindFileInCache(const char *name, unsigned type, char **pathRtrn) 349{ 350 FileCacheEntry *entry; 351 352 for (entry = fileCache; entry != NULL; entry = entry->next) 353 { 354 if ((type == entry->type) && (uStringEqual(name, entry->name))) 355 { 356 *pathRtrn = entry->path; 357 return entry->data; 358 } 359 } 360 return NULL; 361} 362 363/***====================================================================***/ 364 365/** 366 * Search for the given file name in the include directories. 367 * 368 * @param type one of XkbTypesIndex, XkbCompatMapIndex, ..., or 369 * XkbSemanticsFile, XkmKeymapFile, ... 370 * @param pathReturn is set to the full path of the file if found. 371 * 372 * @return an FD to the file or NULL. If NULL is returned, the value of 373 * pathRtrn is undefined. 374 */ 375FILE * 376XkbFindFileInPath(const char *name, unsigned type, char **pathRtrn) 377{ 378 FILE *file = NULL; 379 int nameLen, typeLen; 380 char buf[PATH_MAX]; 381 const char *typeDir; 382 383 typeDir = XkbDirectoryForInclude(type); 384 nameLen = strlen(name); 385 typeLen = strlen(typeDir); 386 for (int i = 0; i < nPathEntries; i++) 387 { 388 int pathLen = strlen(includePath[i]); 389 if (typeLen < 1) 390 continue; 391 392 if ((nameLen + typeLen + pathLen + 2) >= PATH_MAX) 393 { 394 ERROR("File name (%s/%s/%s) too long\n", includePath[i], 395 typeDir, name); 396 ACTION("Ignored\n"); 397 continue; 398 } 399 snprintf(buf, sizeof(buf), "%s/%s/%s", includePath[i], typeDir, name); 400 file = fopen(buf, "r"); 401 if (file != NULL) 402 break; 403 } 404 405 if ((file != NULL) && (pathRtrn != NULL)) 406 { 407 *pathRtrn = strdup(buf); 408 } 409 return file; 410} 411