Add new context API
[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 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <stdio.h>
33 #include <unistd.h>
34
35 #include "xkbcommon/xkbcommon.h"
36 #include "XKBcommonint.h"
37 #include "utils.h"
38
39 struct xkb_context {
40     int refcnt;
41     char **include_paths;
42     int num_include_paths;
43     int size_include_paths;
44 };
45
46 /**
47  * Append one directory to the context's include path.
48  */
49 int
50 xkb_context_include_path_append(struct xkb_context *context, const char *path)
51 {
52     struct stat stat_buf;
53     int err;
54
55     if (context->size_include_paths <= context->num_include_paths) {
56         int new_size;
57         char **new_paths;
58         new_size = context->size_include_paths + 2;
59         new_paths = uTypedRecalloc(context->include_paths,
60                                    context->size_include_paths,
61                                    new_size,
62                                    typeof(new_paths));
63         if (!new_paths)
64             return 0;
65         context->include_paths = new_paths;
66         context->size_include_paths = new_size;
67     }
68
69     err = stat(path, &stat_buf);
70     if (err != 0)
71         return 0;
72     if (!S_ISDIR(stat_buf.st_mode))
73         return 0;
74     if (eaccess(path, R_OK | X_OK) != 0)
75         return 0;
76
77     context->include_paths[context->num_include_paths] = strdup(path);
78     if (!context->include_paths[context->num_include_paths])
79         return 0;
80     context->num_include_paths++;
81
82     return 1;
83 }
84
85 /**
86  * Append the default include directories to the context.
87  */
88 int
89 xkb_context_include_path_append_default(struct xkb_context *context)
90 {
91     const char *home = getenv("HOME");
92     char *user_path;
93     int err;
94
95     (void) xkb_context_include_path_append(context, DFLT_XKB_CONFIG_ROOT);
96
97     home = getenv("HOME");
98     if (!home)
99         return 1;
100     err = asprintf(&user_path, "%s/.xkb", home);
101     if (err <= 0)
102         return 1;
103     (void) xkb_context_include_path_append(context, user_path);
104
105     return 1;
106 }
107
108 /**
109  * Remove all entries in the context's include path.
110  */
111 void
112 xkb_context_include_path_clear(struct xkb_context *context)
113 {
114     int i;
115
116     for (i = 0; i < context->num_include_paths; i++) {
117         free(context->include_paths[i]);
118         context->include_paths[i] = NULL;
119     }
120     context->num_include_paths = 0;
121 }
122
123 /**
124  * xkb_context_include_path_clear() + xkb_context_include_path_append_default()
125  */
126 int
127 xkb_context_include_path_reset_defaults(struct xkb_context *context)
128 {
129     xkb_context_include_path_clear(context);
130     return xkb_context_include_path_append_default(context);
131 }
132
133 /**
134  * Returns the number of entries in the context's include path.
135  */
136 unsigned int
137 xkb_context_include_path_num_entries(struct xkb_context *context)
138 {
139     return context->size_include_paths;
140 }
141
142 /**
143  * Returns the given entry in the context's include path, or NULL if an
144  * invalid index is passed.
145  */
146 const char *
147 xkb_context_include_path_get_entry(struct xkb_context *context,
148                                    unsigned int index)
149 {
150     if (index >= xkb_context_include_path_num_entries(context))
151         return NULL;
152
153     return context->include_paths[index];
154 }
155
156 /**
157  * Take a new reference on the context.
158  */
159 void
160 xkb_context_ref(struct xkb_context *context)
161 {
162     context->refcnt++;
163 }
164
165 /**
166  * Drop an existing reference on the context, and free it if the refcnt is
167  * now 0.
168  */
169 void
170 xkb_context_unref(struct xkb_context *context)
171 {
172     if (--context->refcnt > 0)
173         return;
174
175     xkb_context_include_path_clear(context);
176     free(context);
177 }
178
179 /**
180  * Create a new context.
181  */
182 struct xkb_context *
183 xkb_context_new(void)
184 {
185     struct xkb_context *context = calloc(1, sizeof(*context));
186
187     if (!context)
188         return NULL;
189
190     context->refcnt = 1;
191
192     if (!xkb_context_include_path_append_default(context)) {
193         xkb_context_unref(context);
194         return NULL;
195     }
196
197     return context;
198 }