text: clean up and fix the *MaskText functions
[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 <sys/types.h>
28 #include <sys/stat.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <unistd.h>
32
33 #include "xkbcommon/xkbcommon.h"
34 #include "utils.h"
35 #include "context.h"
36
37 struct xkb_context {
38     int refcnt;
39
40     ATTR_PRINTF(3, 0) void (*log_fn)(struct xkb_context *ctx,
41                                      enum xkb_log_level level,
42                                      const char *fmt, va_list args);
43     enum xkb_log_level log_level;
44     int log_verbosity;
45     void *user_data;
46
47     darray(char *) includes;
48     darray(char *) failed_includes;
49
50     /* xkbcomp needs to assign sequential IDs to XkbFile's it creates. */
51     unsigned file_id;
52
53     struct atom_table *atom_table;
54
55     /* Buffer for the *Text() functions. */
56     char text_buffer[2048];
57     size_t text_next;
58 };
59
60 /**
61  * Append one directory to the context's include path.
62  */
63 XKB_EXPORT int
64 xkb_context_include_path_append(struct xkb_context *ctx, const char *path)
65 {
66     struct stat stat_buf;
67     int err;
68     char *tmp;
69
70     tmp = strdup(path);
71     if (!tmp)
72         goto err;
73
74     err = stat(path, &stat_buf);
75     if (err != 0)
76         goto err;
77     if (!S_ISDIR(stat_buf.st_mode))
78         goto err;
79
80 #if defined(HAVE_EACCESS)
81     if (eaccess(path, R_OK | X_OK) != 0)
82         goto err;
83 #elif defined(HAVE_EUIDACCESS)
84     if (euidaccess(path, R_OK | X_OK) != 0)
85         goto err;
86 #endif
87
88     darray_append(ctx->includes, tmp);
89     return 1;
90
91 err:
92     darray_append(ctx->failed_includes, tmp);
93     return 0;
94 }
95
96 /**
97  * Append the default include directories to the context.
98  */
99 XKB_EXPORT int
100 xkb_context_include_path_append_default(struct xkb_context *ctx)
101 {
102     const char *home;
103     char *user_path;
104     int err;
105     int ret = 0;
106
107     ret |= xkb_context_include_path_append(ctx, DFLT_XKB_CONFIG_ROOT);
108
109     home = getenv("HOME");
110     if (!home)
111         return ret;
112     err = asprintf(&user_path, "%s/.xkb", home);
113     if (err <= 0)
114         return ret;
115     ret |= xkb_context_include_path_append(ctx, user_path);
116     free(user_path);
117
118     return ret;
119 }
120
121 /**
122  * Remove all entries in the context's include path.
123  */
124 XKB_EXPORT void
125 xkb_context_include_path_clear(struct xkb_context *ctx)
126 {
127     char **path;
128
129     darray_foreach(path, ctx->includes)
130         free(*path);
131     darray_free(ctx->includes);
132
133     darray_foreach(path, ctx->failed_includes)
134         free(*path);
135     darray_free(ctx->failed_includes);
136 }
137
138 /**
139  * xkb_context_include_path_clear() + xkb_context_include_path_append_default()
140  */
141 XKB_EXPORT int
142 xkb_context_include_path_reset_defaults(struct xkb_context *ctx)
143 {
144     xkb_context_include_path_clear(ctx);
145     return xkb_context_include_path_append_default(ctx);
146 }
147
148 /**
149  * Returns the number of entries in the context's include path.
150  */
151 XKB_EXPORT unsigned int
152 xkb_context_num_include_paths(struct xkb_context *ctx)
153 {
154     return darray_size(ctx->includes);
155 }
156
157 unsigned int
158 xkb_context_num_failed_include_paths(struct xkb_context *ctx)
159 {
160     return darray_size(ctx->failed_includes);
161 }
162
163 /**
164  * Returns the given entry in the context's include path, or NULL if an
165  * invalid index is passed.
166  */
167 XKB_EXPORT const char *
168 xkb_context_include_path_get(struct xkb_context *ctx, unsigned int idx)
169 {
170     if (idx >= xkb_context_num_include_paths(ctx))
171         return NULL;
172
173     return darray_item(ctx->includes, idx);
174 }
175
176 const char *
177 xkb_context_failed_include_path_get(struct xkb_context *ctx,
178                                     unsigned int idx)
179 {
180     if (idx >= xkb_context_num_failed_include_paths(ctx))
181         return NULL;
182
183     return darray_item(ctx->failed_includes, idx);
184 }
185
186 unsigned
187 xkb_context_take_file_id(struct xkb_context *ctx)
188 {
189     return ctx->file_id++;
190 }
191
192 /**
193  * Take a new reference on the context.
194  */
195 XKB_EXPORT struct xkb_context *
196 xkb_context_ref(struct xkb_context *ctx)
197 {
198     ctx->refcnt++;
199     return ctx;
200 }
201
202 /**
203  * Drop an existing reference on the context, and free it if the refcnt is
204  * now 0.
205  */
206 XKB_EXPORT void
207 xkb_context_unref(struct xkb_context *ctx)
208 {
209     if (!ctx || --ctx->refcnt > 0)
210         return;
211
212     xkb_context_include_path_clear(ctx);
213     atom_table_free(ctx->atom_table);
214     free(ctx);
215 }
216
217 static const char *
218 log_level_to_prefix(enum xkb_log_level level)
219 {
220     switch (level) {
221     case XKB_LOG_LEVEL_DEBUG:
222         return "Debug:";
223     case XKB_LOG_LEVEL_INFO:
224         return "Info:";
225     case XKB_LOG_LEVEL_WARNING:
226         return "Warning:";
227     case XKB_LOG_LEVEL_ERROR:
228         return "Error:";
229     case XKB_LOG_LEVEL_CRITICAL:
230         return "Critical:";
231     default:
232         return NULL;
233     }
234 }
235
236 ATTR_PRINTF(3, 0) static void
237 default_log_fn(struct xkb_context *ctx, enum xkb_log_level level,
238                const char *fmt, va_list args)
239 {
240     const char *prefix = log_level_to_prefix(level);
241
242     if (prefix)
243         fprintf(stderr, "%-10s", prefix);
244     vfprintf(stderr, fmt, args);
245 }
246
247 static enum xkb_log_level
248 log_level(const char *level) {
249     char *endptr;
250     enum xkb_log_level lvl;
251
252     errno = 0;
253     lvl = strtol(level, &endptr, 10);
254     if (errno == 0 && (endptr[0] == '\0' || isspace(endptr[0])))
255         return lvl;
256     if (istreq_prefix("crit", level))
257         return XKB_LOG_LEVEL_CRITICAL;
258     if (istreq_prefix("err", level))
259         return XKB_LOG_LEVEL_ERROR;
260     if (istreq_prefix("warn", level))
261         return XKB_LOG_LEVEL_WARNING;
262     if (istreq_prefix("info", level))
263         return XKB_LOG_LEVEL_INFO;
264     if (istreq_prefix("debug", level) || istreq_prefix("dbg", level))
265         return XKB_LOG_LEVEL_DEBUG;
266
267     return XKB_LOG_LEVEL_ERROR;
268 }
269
270 static int
271 log_verbosity(const char *verbosity) {
272     char *endptr;
273     int v;
274
275     errno = 0;
276     v = strtol(verbosity, &endptr, 10);
277     if (errno == 0)
278         return v;
279
280     return 0;
281 }
282
283 /**
284  * Create a new context.
285  */
286 XKB_EXPORT struct xkb_context *
287 xkb_context_new(enum xkb_context_flags flags)
288 {
289     const char *env;
290     struct xkb_context *ctx = calloc(1, sizeof(*ctx));
291
292     if (!ctx)
293         return NULL;
294
295     ctx->refcnt = 1;
296     ctx->log_fn = default_log_fn;
297     ctx->log_level = XKB_LOG_LEVEL_ERROR;
298     ctx->log_verbosity = 0;
299
300     /* Environment overwrites defaults. */
301     env = getenv("XKB_LOG_LEVEL");
302     if (env)
303         xkb_context_set_log_level(ctx, log_level(env));
304
305     env = getenv("XKB_LOG_VERBOSITY");
306     if (env)
307         xkb_context_set_log_verbosity(ctx, log_verbosity(env));
308
309     if (!(flags & XKB_CONTEXT_NO_DEFAULT_INCLUDES) &&
310         !xkb_context_include_path_append_default(ctx)) {
311         log_err(ctx, "failed to add default include path %s\n",
312                 DFLT_XKB_CONFIG_ROOT);
313         xkb_context_unref(ctx);
314         return NULL;
315     }
316
317     ctx->atom_table = atom_table_new();
318     if (!ctx->atom_table) {
319         xkb_context_unref(ctx);
320         return NULL;
321     }
322
323     return ctx;
324 }
325
326 xkb_atom_t
327 xkb_atom_lookup(struct xkb_context *ctx, const char *string)
328 {
329     return atom_lookup(ctx->atom_table, string);
330 }
331
332 xkb_atom_t
333 xkb_atom_intern(struct xkb_context *ctx, const char *string)
334 {
335     return atom_intern(ctx->atom_table, string, false);
336 }
337
338 xkb_atom_t
339 xkb_atom_steal(struct xkb_context *ctx, char *string)
340 {
341     return atom_intern(ctx->atom_table, string, true);
342 }
343
344 char *
345 xkb_atom_strdup(struct xkb_context *ctx, xkb_atom_t atom)
346 {
347     return atom_strdup(ctx->atom_table, atom);
348 }
349
350 const char *
351 xkb_atom_text(struct xkb_context *ctx, xkb_atom_t atom)
352 {
353     return atom_text(ctx->atom_table, atom);
354 }
355
356 void
357 xkb_log(struct xkb_context *ctx, enum xkb_log_level level,
358         const char *fmt, ...)
359 {
360     va_list args;
361
362     va_start(args, fmt);
363     ctx->log_fn(ctx, level, fmt, args);
364     va_end(args);
365 }
366
367 XKB_EXPORT void
368 xkb_context_set_log_fn(struct xkb_context *ctx,
369                        void (*log_fn)(struct xkb_context *ctx,
370                                       enum xkb_log_level level,
371                                       const char *fmt, va_list args))
372 {
373     ctx->log_fn = (log_fn ? log_fn : default_log_fn);
374 }
375
376 XKB_EXPORT enum xkb_log_level
377 xkb_context_get_log_level(struct xkb_context *ctx)
378 {
379     return ctx->log_level;
380 }
381
382 XKB_EXPORT void
383 xkb_context_set_log_level(struct xkb_context *ctx, enum xkb_log_level level)
384 {
385     ctx->log_level = level;
386 }
387
388 XKB_EXPORT int
389 xkb_context_get_log_verbosity(struct xkb_context *ctx)
390 {
391     return ctx->log_verbosity;
392 }
393
394 XKB_EXPORT void
395 xkb_context_set_log_verbosity(struct xkb_context *ctx, int verbosity)
396 {
397     ctx->log_verbosity = verbosity;
398 }
399
400 XKB_EXPORT void *
401 xkb_context_get_user_data(struct xkb_context *ctx)
402 {
403     if (ctx)
404         return ctx->user_data;
405     return NULL;
406 }
407
408 XKB_EXPORT void
409 xkb_context_set_user_data(struct xkb_context *ctx, void *user_data)
410 {
411     ctx->user_data = user_data;
412 }
413
414 char *
415 xkb_context_get_buffer(struct xkb_context *ctx, size_t size)
416 {
417     char *rtrn;
418
419     if (size >= sizeof(ctx->text_buffer))
420         return NULL;
421
422     if (sizeof(ctx->text_buffer) - ctx->text_next <= size)
423         ctx->text_next = 0;
424
425     rtrn = &ctx->text_buffer[ctx->text_next];
426     ctx->text_next += size;
427
428     return rtrn;
429 }