Contextualize XkbFile IDs
[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 <stdio.h>
29 #include <unistd.h>
30
31 #include "xkb-priv.h"
32
33 struct xkb_context {
34     int refcnt;
35     char **include_paths;
36     int num_include_paths;
37     int size_include_paths;
38
39     /* xkbcomp needs to assign sequential IDs to XkbFile's it creates. */
40     int file_id;
41 };
42
43 /**
44  * Append one directory to the context's include path.
45  */
46 _X_EXPORT int
47 xkb_context_include_path_append(struct xkb_context *context, const char *path)
48 {
49     struct stat stat_buf;
50     int err;
51
52     if (context->size_include_paths <= context->num_include_paths) {
53         int new_size;
54         char **new_paths;
55         new_size = context->size_include_paths + 2;
56         new_paths = uTypedRecalloc(context->include_paths,
57                                    context->size_include_paths,
58                                    new_size,
59                                    char *);
60         if (!new_paths)
61             return 0;
62         context->include_paths = new_paths;
63         context->size_include_paths = new_size;
64     }
65
66     err = stat(path, &stat_buf);
67     if (err != 0)
68         return 0;
69     if (!S_ISDIR(stat_buf.st_mode))
70         return 0;
71     if (eaccess(path, R_OK | X_OK) != 0)
72         return 0;
73
74     context->include_paths[context->num_include_paths] = strdup(path);
75     if (!context->include_paths[context->num_include_paths])
76         return 0;
77     context->num_include_paths++;
78
79     return 1;
80 }
81
82 /**
83  * Append the default include directories to the context.
84  */
85 _X_EXPORT int
86 xkb_context_include_path_append_default(struct xkb_context *context)
87 {
88     const char *home = getenv("HOME");
89     char *user_path;
90     int err;
91
92     (void) xkb_context_include_path_append(context, DFLT_XKB_CONFIG_ROOT);
93
94     home = getenv("HOME");
95     if (!home)
96         return 1;
97     err = asprintf(&user_path, "%s/.xkb", home);
98     if (err <= 0)
99         return 1;
100     (void) xkb_context_include_path_append(context, user_path);
101     free(user_path);
102
103     return 1;
104 }
105
106 /**
107  * Remove all entries in the context's include path.
108  */
109 _X_EXPORT void
110 xkb_context_include_path_clear(struct xkb_context *context)
111 {
112     int i;
113
114     for (i = 0; i < context->num_include_paths; i++) {
115         free(context->include_paths[i]);
116         context->include_paths[i] = NULL;
117     }
118     free(context->include_paths);
119     context->include_paths = NULL;
120     context->num_include_paths = 0;
121 }
122
123 /**
124  * xkb_context_include_path_clear() + xkb_context_include_path_append_default()
125  */
126 _X_EXPORT 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 _X_EXPORT unsigned int
137 xkb_context_num_include_paths(struct xkb_context *context)
138 {
139     return context->num_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 _X_EXPORT const char *
147 xkb_context_include_path_get(struct xkb_context *context, unsigned int idx)
148 {
149     if (idx >= xkb_context_num_include_paths(context))
150         return NULL;
151
152     return context->include_paths[idx];
153 }
154
155 int
156 xkb_context_take_file_id(struct xkb_context *context)
157 {
158     return context->file_id++;
159 }
160
161 /**
162  * Take a new reference on the context.
163  */
164 _X_EXPORT struct xkb_context *
165 xkb_context_ref(struct xkb_context *context)
166 {
167     context->refcnt++;
168     return context;
169 }
170
171 /**
172  * Drop an existing reference on the context, and free it if the refcnt is
173  * now 0.
174  */
175 _X_EXPORT void
176 xkb_context_unref(struct xkb_context *context)
177 {
178     if (--context->refcnt > 0)
179         return;
180
181     xkb_context_include_path_clear(context);
182     free(context);
183 }
184
185 /**
186  * Create a new context.
187  */
188 _X_EXPORT struct xkb_context *
189 xkb_context_new(enum xkb_context_flags flags)
190 {
191     struct xkb_context *context = calloc(1, sizeof(*context));
192
193     if (!context)
194         return NULL;
195
196     context->refcnt = 1;
197
198     if (!(flags & XKB_CONTEXT_NO_DEFAULT_INCLUDES) &&
199         !xkb_context_include_path_append_default(context)) {
200         xkb_context_unref(context);
201         return NULL;
202     }
203
204     return context;
205 }