6c52d37b3126a9507007f3762829facb65dc6343
[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 priority_to_prefix(int priority)
180 {
181     switch (priority) {
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 "Internal error (critical):";
192     default:
193         return NULL;
194     }
195 }
196
197 ATTR_PRINTF(3, 0) static void
198 default_log_fn(struct xkb_context *ctx, int priority,
199                const char *fmt, va_list args)
200 {
201     const char *prefix = priority_to_prefix(priority);
202
203     if (prefix)
204         fprintf(stderr, "%-10s", prefix);
205     vfprintf(stderr, fmt, args);
206 }
207
208 static int
209 log_priority(const char *priority) {
210     char *endptr;
211     int prio;
212
213     errno = 0;
214     prio = strtol(priority, &endptr, 10);
215     if (errno == 0 && (endptr[0] == '\0' || isspace(endptr[0])))
216         return prio;
217     if (strncasecmp(priority, "err", 3) == 0)
218         return XKB_LOG_LEVEL_ERROR;
219     if (strncasecmp(priority, "warn", 4) == 0)
220         return XKB_LOG_LEVEL_WARNING;
221     if (strncasecmp(priority, "info", 4) == 0)
222         return XKB_LOG_LEVEL_INFO;
223     if (strncasecmp(priority, "debug", 5) == 0)
224         return XKB_LOG_LEVEL_DEBUG;
225
226     return XKB_LOG_LEVEL_ERROR;
227 }
228
229 static int
230 log_verbosity(const char *verbosity) {
231     char *endptr;
232     int v;
233
234     errno = 0;
235     v = strtol(verbosity, &endptr, 10);
236     if (errno == 0)
237         return v;
238
239     return 0;
240 }
241
242 /**
243  * Create a new context.
244  */
245 XKB_EXPORT struct xkb_context *
246 xkb_context_new(enum xkb_context_flags flags)
247 {
248     const char *env;
249     struct xkb_context *ctx = calloc(1, sizeof(*ctx));
250
251     if (!ctx)
252         return NULL;
253
254     ctx->refcnt = 1;
255     ctx->log_fn = default_log_fn;
256     ctx->log_priority = XKB_LOG_LEVEL_ERROR;
257     ctx->log_verbosity = 0;
258
259     /* Environment overwrites defaults. */
260     env = getenv("XKB_LOG");
261     if (env)
262         xkb_set_log_priority(ctx, log_priority(env));
263
264     env = getenv("XKB_VERBOSITY");
265     if (env)
266         xkb_set_log_verbosity(ctx, log_verbosity(env));
267
268     if (!(flags & XKB_CONTEXT_NO_DEFAULT_INCLUDES) &&
269         !xkb_context_include_path_append_default(ctx)) {
270         log_err(ctx, "failed to add default include path %s\n",
271                 DFLT_XKB_CONFIG_ROOT);
272         xkb_context_unref(ctx);
273         return NULL;
274     }
275
276     ctx->atom_table = atom_table_new();
277     if (!ctx->atom_table) {
278         xkb_context_unref(ctx);
279         return NULL;
280     }
281
282     return ctx;
283 }
284
285 xkb_atom_t
286 xkb_atom_intern(struct xkb_context *ctx, const char *string)
287 {
288     return atom_intern(ctx->atom_table, string, false);
289 }
290
291 xkb_atom_t
292 xkb_atom_steal(struct xkb_context *ctx, char *string)
293 {
294     return atom_intern(ctx->atom_table, string, true);
295 }
296
297 char *
298 xkb_atom_strdup(struct xkb_context *ctx, xkb_atom_t atom)
299 {
300     return atom_strdup(ctx->atom_table, atom);
301 }
302
303 const char *
304 xkb_atom_text(struct xkb_context *ctx, xkb_atom_t atom)
305 {
306     return atom_text(ctx->atom_table, atom);
307 }
308
309 void
310 xkb_log(struct xkb_context *ctx, int priority, const char *fmt, ...)
311 {
312     va_list args;
313
314     va_start(args, fmt);
315     ctx->log_fn(ctx, priority, fmt, args);
316     va_end(args);
317 }
318
319 XKB_EXPORT void
320 xkb_set_log_fn(struct xkb_context *ctx,
321                void (*log_fn)(struct xkb_context *ctx, int priority,
322                               const char *fmt, va_list args))
323 {
324     ctx->log_fn = (log_fn ? log_fn : default_log_fn);
325 }
326
327 XKB_EXPORT enum xkb_log_level
328 xkb_get_log_priority(struct xkb_context *ctx)
329 {
330     return ctx->log_priority;
331 }
332
333 XKB_EXPORT void
334 xkb_set_log_priority(struct xkb_context *ctx, enum xkb_log_level priority)
335 {
336     ctx->log_priority = priority;
337 }
338
339 XKB_EXPORT int
340 xkb_get_log_verbosity(struct xkb_context *ctx)
341 {
342     return ctx->log_verbosity;
343 }
344
345 XKB_EXPORT void
346 xkb_set_log_verbosity(struct xkb_context *ctx, int verbosity)
347 {
348     ctx->log_verbosity = verbosity;
349 }
350
351 XKB_EXPORT void *
352 xkb_get_user_data(struct xkb_context *ctx)
353 {
354     if (ctx)
355         return ctx->user_data;
356     return NULL;
357 }
358
359 XKB_EXPORT void
360 xkb_set_user_data(struct xkb_context *ctx, void *user_data)
361 {
362     ctx->user_data = user_data;
363 }