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