• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@fooishbar.org>
25  */
26 
27 #include "config.h"
28 
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <errno.h>
32 #ifdef _MSC_VER
33 # include <direct.h>
34 # include <io.h>
35 # ifndef S_ISDIR
36 #  define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
37 # endif
38 #else
39 # include <unistd.h>
40 #endif
41 
42 #include "xkbcommon/xkbcommon.h"
43 #include "utils.h"
44 #include "context.h"
45 
46 /**
47  * Append one directory to the context's include path.
48  */
49 XKB_EXPORT int
xkb_context_include_path_append(struct xkb_context * ctx,const char * path)50 xkb_context_include_path_append(struct xkb_context *ctx, const char *path)
51 {
52     struct stat stat_buf;
53     int err = ENOMEM;
54     char *tmp;
55 
56     tmp = strdup(path);
57     if (!tmp)
58         goto err;
59 
60     err = stat(path, &stat_buf);
61     if (err != 0) {
62         err = errno;
63         goto err;
64     }
65     if (!S_ISDIR(stat_buf.st_mode)) {
66         err = ENOTDIR;
67         goto err;
68     }
69 
70     if (!check_eaccess(path, R_OK | X_OK)) {
71         err = EACCES;
72         goto err;
73     }
74 
75     darray_append(ctx->includes, tmp);
76     log_dbg(ctx, "Include path added: %s\n", tmp);
77 
78     return 1;
79 
80 err:
81     darray_append(ctx->failed_includes, tmp);
82     log_dbg(ctx, "Include path failed: %s (%s)\n", tmp, strerror(err));
83     return 0;
84 }
85 
86 const char *
xkb_context_include_path_get_extra_path(struct xkb_context * ctx)87 xkb_context_include_path_get_extra_path(struct xkb_context *ctx)
88 {
89     const char *extra = secure_getenv("XKB_CONFIG_EXTRA_PATH");
90     return extra ? extra : DFLT_XKB_CONFIG_EXTRA_PATH;
91 }
92 
93 const char *
xkb_context_include_path_get_system_path(struct xkb_context * ctx)94 xkb_context_include_path_get_system_path(struct xkb_context *ctx)
95 {
96     const char *root = secure_getenv("XKB_CONFIG_ROOT");
97     return root ? root : DFLT_XKB_CONFIG_ROOT;
98 }
99 
100 /**
101  * Append the default include directories to the context.
102  */
103 XKB_EXPORT int
xkb_context_include_path_append_default(struct xkb_context * ctx)104 xkb_context_include_path_append_default(struct xkb_context *ctx)
105 {
106     const char *home, *xdg, *root, *extra;
107     char *user_path;
108     int ret = 0;
109 
110     home = secure_getenv("HOME");
111 
112     xdg = secure_getenv("XDG_CONFIG_HOME");
113     if (xdg != NULL) {
114         user_path = asprintf_safe("%s/xkb", xdg);
115         if (user_path) {
116             ret |= xkb_context_include_path_append(ctx, user_path);
117             free(user_path);
118         }
119     } else if (home != NULL) {
120         /* XDG_CONFIG_HOME fallback is $HOME/.config/ */
121         user_path = asprintf_safe("%s/.config/xkb", home);
122         if (user_path) {
123             ret |= xkb_context_include_path_append(ctx, user_path);
124             free(user_path);
125         }
126     }
127 
128     if (home != NULL) {
129         user_path = asprintf_safe("%s/.xkb", home);
130         if (user_path) {
131             ret |= xkb_context_include_path_append(ctx, user_path);
132             free(user_path);
133         }
134     }
135 
136     extra = xkb_context_include_path_get_extra_path(ctx);
137     ret |= xkb_context_include_path_append(ctx, extra);
138     root = xkb_context_include_path_get_system_path(ctx);
139     ret |= xkb_context_include_path_append(ctx, root);
140 
141     return ret;
142 }
143 
144 /**
145  * Remove all entries in the context's include path.
146  */
147 XKB_EXPORT void
xkb_context_include_path_clear(struct xkb_context * ctx)148 xkb_context_include_path_clear(struct xkb_context *ctx)
149 {
150     char **path;
151 
152     darray_foreach(path, ctx->includes)
153         free(*path);
154     darray_free(ctx->includes);
155 
156     darray_foreach(path, ctx->failed_includes)
157         free(*path);
158     darray_free(ctx->failed_includes);
159 }
160 
161 /**
162  * xkb_context_include_path_clear() + xkb_context_include_path_append_default()
163  */
164 XKB_EXPORT int
xkb_context_include_path_reset_defaults(struct xkb_context * ctx)165 xkb_context_include_path_reset_defaults(struct xkb_context *ctx)
166 {
167     xkb_context_include_path_clear(ctx);
168     return xkb_context_include_path_append_default(ctx);
169 }
170 
171 /**
172  * Returns the number of entries in the context's include path.
173  */
174 XKB_EXPORT unsigned int
xkb_context_num_include_paths(struct xkb_context * ctx)175 xkb_context_num_include_paths(struct xkb_context *ctx)
176 {
177     return darray_size(ctx->includes);
178 }
179 
180 /**
181  * Returns the given entry in the context's include path, or NULL if an
182  * invalid index is passed.
183  */
184 XKB_EXPORT const char *
xkb_context_include_path_get(struct xkb_context * ctx,unsigned int idx)185 xkb_context_include_path_get(struct xkb_context *ctx, unsigned int idx)
186 {
187     if (idx >= xkb_context_num_include_paths(ctx))
188         return NULL;
189 
190     return darray_item(ctx->includes, idx);
191 }
192 
193 /**
194  * Take a new reference on the context.
195  */
196 XKB_EXPORT struct xkb_context *
xkb_context_ref(struct xkb_context * ctx)197 xkb_context_ref(struct xkb_context *ctx)
198 {
199     ctx->refcnt++;
200     return ctx;
201 }
202 
203 /**
204  * Drop an existing reference on the context, and free it if the refcnt is
205  * now 0.
206  */
207 XKB_EXPORT void
xkb_context_unref(struct xkb_context * ctx)208 xkb_context_unref(struct xkb_context *ctx)
209 {
210     if (!ctx || --ctx->refcnt > 0)
211         return;
212 
213     free(ctx->x11_atom_cache);
214     xkb_context_include_path_clear(ctx);
215     atom_table_free(ctx->atom_table);
216     free(ctx);
217 }
218 
219 static const char *
log_level_to_prefix(enum xkb_log_level level)220 log_level_to_prefix(enum xkb_log_level level)
221 {
222     switch (level) {
223     case XKB_LOG_LEVEL_DEBUG:
224         return "xkbcommon: DEBUG: ";
225     case XKB_LOG_LEVEL_INFO:
226         return "xkbcommon: INFO: ";
227     case XKB_LOG_LEVEL_WARNING:
228         return "xkbcommon: WARNING: ";
229     case XKB_LOG_LEVEL_ERROR:
230         return "xkbcommon: ERROR: ";
231     case XKB_LOG_LEVEL_CRITICAL:
232         return "xkbcommon: CRITICAL: ";
233     default:
234         return NULL;
235     }
236 }
237 
238 ATTR_PRINTF(3, 0) static void
default_log_fn(struct xkb_context * ctx,enum xkb_log_level level,const char * fmt,va_list args)239 default_log_fn(struct xkb_context *ctx, enum xkb_log_level level,
240                const char *fmt, va_list args)
241 {
242     const char *prefix = log_level_to_prefix(level);
243 
244     if (prefix)
245         fprintf(stderr, "%s", prefix);
246     vfprintf(stderr, fmt, args);
247 }
248 
249 static enum xkb_log_level
log_level(const char * level)250 log_level(const char *level) {
251     char *endptr;
252     enum xkb_log_level lvl;
253 
254     errno = 0;
255     lvl = strtol(level, &endptr, 10);
256     if (errno == 0 && (endptr[0] == '\0' || is_space(endptr[0])))
257         return lvl;
258     if (istreq_prefix("crit", level))
259         return XKB_LOG_LEVEL_CRITICAL;
260     if (istreq_prefix("err", level))
261         return XKB_LOG_LEVEL_ERROR;
262     if (istreq_prefix("warn", level))
263         return XKB_LOG_LEVEL_WARNING;
264     if (istreq_prefix("info", level))
265         return XKB_LOG_LEVEL_INFO;
266     if (istreq_prefix("debug", level) || istreq_prefix("dbg", level))
267         return XKB_LOG_LEVEL_DEBUG;
268 
269     return XKB_LOG_LEVEL_ERROR;
270 }
271 
272 static int
log_verbosity(const char * verbosity)273 log_verbosity(const char *verbosity) {
274     char *endptr;
275     int v;
276 
277     errno = 0;
278     v = strtol(verbosity, &endptr, 10);
279     if (errno == 0)
280         return v;
281 
282     return 0;
283 }
284 
285 /**
286  * Create a new context.
287  */
288 XKB_EXPORT struct xkb_context *
xkb_context_new(enum xkb_context_flags flags)289 xkb_context_new(enum xkb_context_flags flags)
290 {
291     const char *env;
292     struct xkb_context *ctx = calloc(1, sizeof(*ctx));
293 
294     if (!ctx)
295         return NULL;
296 
297     ctx->refcnt = 1;
298     ctx->log_fn = default_log_fn;
299     ctx->log_level = XKB_LOG_LEVEL_ERROR;
300     ctx->log_verbosity = 0;
301 
302     /* Environment overwrites defaults. */
303     env = secure_getenv("XKB_LOG_LEVEL");
304     if (env)
305         xkb_context_set_log_level(ctx, log_level(env));
306 
307     env = secure_getenv("XKB_LOG_VERBOSITY");
308     if (env)
309         xkb_context_set_log_verbosity(ctx, log_verbosity(env));
310 
311     if (!(flags & XKB_CONTEXT_NO_DEFAULT_INCLUDES) &&
312         !xkb_context_include_path_append_default(ctx)) {
313         log_err(ctx, "failed to add default include path %s\n",
314                 DFLT_XKB_CONFIG_ROOT);
315         xkb_context_unref(ctx);
316         return NULL;
317     }
318 
319     ctx->use_environment_names = !(flags & XKB_CONTEXT_NO_ENVIRONMENT_NAMES);
320 
321     ctx->atom_table = atom_table_new();
322     if (!ctx->atom_table) {
323         xkb_context_unref(ctx);
324         return NULL;
325     }
326 
327     ctx->x11_atom_cache = NULL;
328 
329     return ctx;
330 }
331 
332 XKB_EXPORT void
xkb_context_set_log_fn(struct xkb_context * ctx,void (* log_fn)(struct xkb_context * ctx,enum xkb_log_level level,const char * fmt,va_list args))333 xkb_context_set_log_fn(struct xkb_context *ctx,
334                        void (*log_fn)(struct xkb_context *ctx,
335                                       enum xkb_log_level level,
336                                       const char *fmt, va_list args))
337 {
338     ctx->log_fn = (log_fn ? log_fn : default_log_fn);
339 }
340 
341 XKB_EXPORT enum xkb_log_level
xkb_context_get_log_level(struct xkb_context * ctx)342 xkb_context_get_log_level(struct xkb_context *ctx)
343 {
344     return ctx->log_level;
345 }
346 
347 XKB_EXPORT void
xkb_context_set_log_level(struct xkb_context * ctx,enum xkb_log_level level)348 xkb_context_set_log_level(struct xkb_context *ctx, enum xkb_log_level level)
349 {
350     ctx->log_level = level;
351 }
352 
353 XKB_EXPORT int
xkb_context_get_log_verbosity(struct xkb_context * ctx)354 xkb_context_get_log_verbosity(struct xkb_context *ctx)
355 {
356     return ctx->log_verbosity;
357 }
358 
359 XKB_EXPORT void
xkb_context_set_log_verbosity(struct xkb_context * ctx,int verbosity)360 xkb_context_set_log_verbosity(struct xkb_context *ctx, int verbosity)
361 {
362     ctx->log_verbosity = verbosity;
363 }
364 
365 XKB_EXPORT void *
xkb_context_get_user_data(struct xkb_context * ctx)366 xkb_context_get_user_data(struct xkb_context *ctx)
367 {
368     if (ctx)
369         return ctx->user_data;
370     return NULL;
371 }
372 
373 XKB_EXPORT void
xkb_context_set_user_data(struct xkb_context * ctx,void * user_data)374 xkb_context_set_user_data(struct xkb_context *ctx, void *user_data)
375 {
376     ctx->user_data = user_data;
377 }
378