1 /* 2 * Copyright 2012 Intel Corporation 3 * Copyright 2012 Ran Benita 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the next 13 * paragraph) shall be included in all copies or substantial portions of the 14 * Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 * DEALINGS IN THE SOFTWARE. 23 * 24 * Author: Daniel Stone <daniel (at) fooishbar.org> 25 */ 26 27 #include <sys/types.h> 28 #include <sys/stat.h> 29 #include <errno.h> 30 #include <unistd.h> 31 32 #include "xkbcommon/xkbcommon.h" 33 #include "utils.h" 34 #include "context.h" 35 36 /** 37 * Append one directory to the context's include path. 38 */ 39 XKB_EXPORT int 40 xkb_context_include_path_append(struct xkb_context *ctx, const char *path) 41 { 42 struct stat stat_buf; 43 int err; 44 char *tmp; 45 46 tmp = strdup(path); 47 if (!tmp) 48 goto err; 49 50 err = stat(path, &stat_buf); 51 if (err != 0) 52 goto err; 53 if (!S_ISDIR(stat_buf.st_mode)) 54 goto err; 55 56 #if defined(HAVE_EACCESS) 57 if (eaccess(path, R_OK | X_OK) != 0) 58 goto err; 59 #elif defined(HAVE_EUIDACCESS) 60 if (euidaccess(path, R_OK | X_OK) != 0) 61 goto err; 62 #endif 63 64 darray_append(ctx->includes, tmp); 65 return 1; 66 67 err: 68 darray_append(ctx->failed_includes, tmp); 69 return 0; 70 } 71 72 /** 73 * Append the default include directories to the context. 74 */ 75 XKB_EXPORT int 76 xkb_context_include_path_append_default(struct xkb_context *ctx) 77 { 78 const char *home; 79 char *user_path; 80 int err; 81 int ret = 0; 82 83 ret |= xkb_context_include_path_append(ctx, DFLT_XKB_CONFIG_ROOT); 84 85 home = secure_getenv("HOME"); 86 if (!home) 87 return ret; 88 err = asprintf(&user_path, "%s/.xkb", home); 89 if (err <= 0) 90 return ret; 91 ret |= xkb_context_include_path_append(ctx, user_path); 92 free(user_path); 93 94 return ret; 95 } 96 97 /** 98 * Remove all entries in the context's include path. 99 */ 100 XKB_EXPORT void 101 xkb_context_include_path_clear(struct xkb_context *ctx) 102 { 103 char **path; 104 105 darray_foreach(path, ctx->includes) 106 free(*path); 107 darray_free(ctx->includes); 108 109 darray_foreach(path, ctx->failed_includes) 110 free(*path); 111 darray_free(ctx->failed_includes); 112 } 113 114 /** 115 * xkb_context_include_path_clear() + xkb_context_include_path_append_default() 116 */ 117 XKB_EXPORT int 118 xkb_context_include_path_reset_defaults(struct xkb_context *ctx) 119 { 120 xkb_context_include_path_clear(ctx); 121 return xkb_context_include_path_append_default(ctx); 122 } 123 124 /** 125 * Returns the number of entries in the context's include path. 126 */ 127 XKB_EXPORT unsigned int 128 xkb_context_num_include_paths(struct xkb_context *ctx) 129 { 130 return darray_size(ctx->includes); 131 } 132 133 /** 134 * Returns the given entry in the context's include path, or NULL if an 135 * invalid index is passed. 136 */ 137 XKB_EXPORT const char * 138 xkb_context_include_path_get(struct xkb_context *ctx, unsigned int idx) 139 { 140 if (idx >= xkb_context_num_include_paths(ctx)) 141 return NULL; 142 143 return darray_item(ctx->includes, idx); 144 } 145 146 /** 147 * Take a new reference on the context. 148 */ 149 XKB_EXPORT struct xkb_context * 150 xkb_context_ref(struct xkb_context *ctx) 151 { 152 ctx->refcnt++; 153 return ctx; 154 } 155 156 /** 157 * Drop an existing reference on the context, and free it if the refcnt is 158 * now 0. 159 */ 160 XKB_EXPORT void 161 xkb_context_unref(struct xkb_context *ctx) 162 { 163 if (!ctx || --ctx->refcnt > 0) 164 return; 165 166 xkb_context_include_path_clear(ctx); 167 atom_table_free(ctx->atom_table); 168 free(ctx); 169 } 170 171 static const char * 172 log_level_to_prefix(enum xkb_log_level level) 173 { 174 switch (level) { 175 case XKB_LOG_LEVEL_DEBUG: 176 return "xkbcommon: DEBUG: "; 177 case XKB_LOG_LEVEL_INFO: 178 return "xkbcommon: INFO: "; 179 case XKB_LOG_LEVEL_WARNING: 180 return "xkbcommon: WARNING: "; 181 case XKB_LOG_LEVEL_ERROR: 182 return "xkbcommon: ERROR: "; 183 case XKB_LOG_LEVEL_CRITICAL: 184 return "xkbcommon: CRITICAL: "; 185 default: 186 return NULL; 187 } 188 } 189 190 ATTR_PRINTF(3, 0) static void 191 default_log_fn(struct xkb_context *ctx, enum xkb_log_level level, 192 const char *fmt, va_list args) 193 { 194 const char *prefix = log_level_to_prefix(level); 195 196 if (prefix) 197 fprintf(stderr, "%s", prefix); 198 vfprintf(stderr, fmt, args); 199 } 200 201 static enum xkb_log_level 202 log_level(const char *level) { 203 char *endptr; 204 enum xkb_log_level lvl; 205 206 errno = 0; 207 lvl = strtol(level, &endptr, 10); 208 if (errno == 0 && (endptr[0] == '\0' || is_space(endptr[0]))) 209 return lvl; 210 if (istreq_prefix("crit", level)) 211 return XKB_LOG_LEVEL_CRITICAL; 212 if (istreq_prefix("err", level)) 213 return XKB_LOG_LEVEL_ERROR; 214 if (istreq_prefix("warn", level)) 215 return XKB_LOG_LEVEL_WARNING; 216 if (istreq_prefix("info", level)) 217 return XKB_LOG_LEVEL_INFO; 218 if (istreq_prefix("debug", level) || istreq_prefix("dbg", level)) 219 return XKB_LOG_LEVEL_DEBUG; 220 221 return XKB_LOG_LEVEL_ERROR; 222 } 223 224 static int 225 log_verbosity(const char *verbosity) { 226 char *endptr; 227 int v; 228 229 errno = 0; 230 v = strtol(verbosity, &endptr, 10); 231 if (errno == 0) 232 return v; 233 234 return 0; 235 } 236 237 /** 238 * Create a new context. 239 */ 240 XKB_EXPORT struct xkb_context * 241 xkb_context_new(enum xkb_context_flags flags) 242 { 243 const char *env; 244 struct xkb_context *ctx = calloc(1, sizeof(*ctx)); 245 246 if (!ctx) 247 return NULL; 248 249 ctx->refcnt = 1; 250 ctx->log_fn = default_log_fn; 251 ctx->log_level = XKB_LOG_LEVEL_ERROR; 252 ctx->log_verbosity = 0; 253 254 /* Environment overwrites defaults. */ 255 env = secure_getenv("XKB_LOG_LEVEL"); 256 if (env) 257 xkb_context_set_log_level(ctx, log_level(env)); 258 259 env = secure_getenv("XKB_LOG_VERBOSITY"); 260 if (env) 261 xkb_context_set_log_verbosity(ctx, log_verbosity(env)); 262 263 if (!(flags & XKB_CONTEXT_NO_DEFAULT_INCLUDES) && 264 !xkb_context_include_path_append_default(ctx)) { 265 log_err(ctx, "failed to add default include path %s\n", 266 DFLT_XKB_CONFIG_ROOT); 267 xkb_context_unref(ctx); 268 return NULL; 269 } 270 271 ctx->use_environment_names = !(flags & XKB_CONTEXT_NO_ENVIRONMENT_NAMES); 272 273 ctx->atom_table = atom_table_new(); 274 if (!ctx->atom_table) { 275 xkb_context_unref(ctx); 276 return NULL; 277 } 278 279 return ctx; 280 } 281 282 XKB_EXPORT void 283 xkb_context_set_log_fn(struct xkb_context *ctx, 284 void (*log_fn)(struct xkb_context *ctx, 285 enum xkb_log_level level, 286 const char *fmt, va_list args)) 287 { 288 ctx->log_fn = (log_fn ? log_fn : default_log_fn); 289 } 290 291 XKB_EXPORT enum xkb_log_level 292 xkb_context_get_log_level(struct xkb_context *ctx) 293 { 294 return ctx->log_level; 295 } 296 297 XKB_EXPORT void 298 xkb_context_set_log_level(struct xkb_context *ctx, enum xkb_log_level level) 299 { 300 ctx->log_level = level; 301 } 302 303 XKB_EXPORT int 304 xkb_context_get_log_verbosity(struct xkb_context *ctx) 305 { 306 return ctx->log_verbosity; 307 } 308 309 XKB_EXPORT void 310 xkb_context_set_log_verbosity(struct xkb_context *ctx, int verbosity) 311 { 312 ctx->log_verbosity = verbosity; 313 } 314 315 XKB_EXPORT void * 316 xkb_context_get_user_data(struct xkb_context *ctx) 317 { 318 if (ctx) 319 return ctx->user_data; 320 return NULL; 321 } 322 323 XKB_EXPORT void 324 xkb_context_set_user_data(struct xkb_context *ctx, void *user_data) 325 { 326 ctx->user_data = user_data; 327 } 328