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