Don't leak include_paths in context
[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     free(user_path);
105
106     return 1;
107 }
108
109 /**
110  * Remove all entries in the context's include path.
111  */
112 void
113 xkb_context_include_path_clear(struct xkb_context *context)
114 {
115     int i;
116
117     for (i = 0; i < context->num_include_paths; i++) {
118         free(context->include_paths[i]);
119         context->include_paths[i] = NULL;
120     }
121     free(context->include_paths);
122     context->include_paths = NULL;
123     context->num_include_paths = 0;
124 }
125
126 /**
127  * xkb_context_include_path_clear() + xkb_context_include_path_append_default()
128  */
129 int
130 xkb_context_include_path_reset_defaults(struct xkb_context *context)
131 {
132     xkb_context_include_path_clear(context);
133     return xkb_context_include_path_append_default(context);
134 }
135
136 /**
137  * Returns the number of entries in the context's include path.
138  */
139 unsigned int
140 xkb_context_num_include_paths(struct xkb_context *context)
141 {
142     return context->num_include_paths;
143 }
144
145 /**
146  * Returns the given entry in the context's include path, or NULL if an
147  * invalid index is passed.
148  */
149 const char *
150 xkb_context_include_path_get(struct xkb_context *context, unsigned int index)
151 {
152     if (index >= xkb_context_num_include_paths(context))
153         return NULL;
154
155     return context->include_paths[index];
156 }
157
158 /**
159  * Take a new reference on the context.
160  */
161 void
162 xkb_context_ref(struct xkb_context *context)
163 {
164     context->refcnt++;
165 }
166
167 /**
168  * Drop an existing reference on the context, and free it if the refcnt is
169  * now 0.
170  */
171 void
172 xkb_context_unref(struct xkb_context *context)
173 {
174     if (--context->refcnt > 0)
175         return;
176
177     xkb_context_include_path_clear(context);
178     free(context);
179 }
180
181 /**
182  * Create a new context.
183  */
184 struct xkb_context *
185 xkb_context_new(void)
186 {
187     struct xkb_context *context = calloc(1, sizeof(*context));
188
189     if (!context)
190         return NULL;
191
192     context->refcnt = 1;
193
194     if (!xkb_context_include_path_append_default(context)) {
195         xkb_context_unref(context);
196         return NULL;
197     }
198
199     return context;
200 }