Allocate xkb_component_names on stack
[platform/upstream/libxkbcommon.git] / src / xkbcomp / path.c
1 /************************************************************
2  * Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
3  *
4  * Permission to use, copy, modify, and distribute this
5  * software and its documentation for any purpose and without
6  * fee is hereby granted, provided that the above copyright
7  * notice appear in all copies and that both that copyright
8  * notice and this permission notice appear in supporting
9  * documentation, and that the name of Silicon Graphics not be
10  * used in advertising or publicity pertaining to distribution
11  * of the software without specific prior written permission.
12  * Silicon Graphics makes no representation about the suitability
13  * of this software for any purpose. It is provided "as is"
14  * without any express or implied warranty.
15  *
16  * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18  * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19  * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
23  * THE USE OR PERFORMANCE OF THIS SOFTWARE.
24  *
25  ********************************************************/
26
27 #include <errno.h>
28 #include <limits.h>
29
30 #include "path.h"
31 #include "parseutils.h"
32
33 /**
34  * Extract the first token from an include statement.
35  * @param str_inout Input statement, modified in-place. Can be passed in
36  * repeatedly. If str_inout is NULL, the parsing has completed.
37  * @param file_rtrn Set to the include file to be used.
38  * @param map_rtrn Set to whatever comes after ), if any.
39  * @param nextop_rtrn Set to the next operation in the complete statement.
40  * @param extra_data Set to the string between ( and ), if any.
41  *
42  * @return true if parsing was succcessful, false for an illegal string.
43  *
44  * Example: "evdev+aliases(qwerty)"
45  *      str_inout = aliases(qwerty)
46  *      nextop_retrn = +
47  *      extra_data = NULL
48  *      file_rtrn = evdev
49  *      map_rtrn = NULL
50  *
51  * 2nd run with "aliases(qwerty)"
52  *      str_inout = NULL
53  *      file_rtrn = aliases
54  *      map_rtrn = qwerty
55  *      extra_data = NULL
56  *      nextop_retrn = ""
57  *
58  */
59 bool
60 XkbParseIncludeMap(char **str_inout, char **file_rtrn, char **map_rtrn,
61                    char *nextop_rtrn, char **extra_data)
62 {
63     char *tmp, *str, *next;
64
65     str = *str_inout;
66
67     /* search for tokens inside the string */
68     next = strpbrk(str, "|+");
69     if (next) {
70         /* set nextop_rtrn to \0, next to next character */
71         *nextop_rtrn = *next;
72         *next++ = '\0';
73     }
74     else {
75         *nextop_rtrn = '\0';
76         next = NULL;
77     }
78
79     /* search for :, store result in extra_data */
80     tmp = strchr(str, ':');
81     if (tmp != NULL) {
82         *tmp++ = '\0';
83         *extra_data = strdup(tmp);
84     }
85     else {
86         *extra_data = NULL;
87     }
88
89     tmp = strchr(str, '(');
90     if (tmp == NULL) {
91         *file_rtrn = strdup(str);
92         *map_rtrn = NULL;
93     }
94     else if (str[0] == '(') {
95         free(*extra_data);
96         return false;
97     }
98     else {
99         *tmp++ = '\0';
100         *file_rtrn = strdup(str);
101         str = tmp;
102         tmp = strchr(str, ')');
103         if ((tmp == NULL) || (tmp[1] != '\0')) {
104             free(*file_rtrn);
105             free(*extra_data);
106             return false;
107         }
108         *tmp++ = '\0';
109         *map_rtrn = strdup(str);
110     }
111
112     if (*nextop_rtrn == '\0')
113         *str_inout = NULL;
114     else if ((*nextop_rtrn == '|') || (*nextop_rtrn == '+'))
115         *str_inout = next;
116     else
117         return false;
118
119     return true;
120 }
121
122 /***====================================================================***/
123
124 /**
125  * Return the xkb directory based on the type.
126  */
127 const char *
128 XkbDirectoryForInclude(enum xkb_file_type type)
129 {
130     switch (type) {
131     case FILE_TYPE_KEYMAP:
132         return "keymap";
133
134     case FILE_TYPE_KEYCODES:
135         return "keycodes";
136
137     case FILE_TYPE_TYPES:
138         return "types";
139
140     case FILE_TYPE_SYMBOLS:
141         return "symbols";
142
143     case FILE_TYPE_COMPAT:
144         return "compat";
145
146     case FILE_TYPE_GEOMETRY:
147         return "geometry";
148
149     case FILE_TYPE_RULES:
150         return "rules";
151
152     default:
153         return "";
154     }
155 }
156
157 /***====================================================================***/
158
159 /**
160  * Search for the given file name in the include directories.
161  *
162  * @param ctx the XKB ctx containing the include paths
163  * @param type one of FILE_TYPE_TYPES, FILE_TYPE_COMPAT, ..., or
164  *             FILE_TYPE_KEYMAP or FILE_TYPE_RULES
165  * @param pathRtrn is set to the full path of the file if found.
166  *
167  * @return an FD to the file or NULL. If NULL is returned, the value of
168  * pathRtrn is undefined.
169  */
170 FILE *
171 XkbFindFileInPath(struct xkb_context *ctx, const char *name,
172                   enum xkb_file_type type, char **pathRtrn)
173 {
174     size_t i;
175     int ret;
176     FILE *file = NULL;
177     char buf[PATH_MAX];
178     const char *typeDir;
179
180     typeDir = XkbDirectoryForInclude(type);
181     for (i = 0; i < xkb_context_num_include_paths(ctx); i++) {
182         ret = snprintf(buf, sizeof(buf), "%s/%s/%s",
183                        xkb_context_include_path_get(ctx, i), typeDir, name);
184         if (ret >= (ssize_t) sizeof(buf)) {
185             log_err(ctx, "File name (%s/%s/%s) too long\n",
186                     xkb_context_include_path_get(ctx, i), typeDir, name);
187             continue;
188         }
189         file = fopen(buf, "r");
190         if (file == NULL) {
191             log_err(ctx, "Couldn't open file (%s/%s/%s): %s\n",
192                     xkb_context_include_path_get(ctx, i), typeDir, name,
193                     strerror(errno));
194             continue;
195         }
196         break;
197     }
198
199     if ((file != NULL) && (pathRtrn != NULL))
200         *pathRtrn = strdup(buf);
201     return file;
202 }
203
204 /**
205  * Open the file given in the include statement and parse it's content.
206  * If the statement defines a specific map to use, this map is returned in
207  * file_rtrn. Otherwise, the default map is returned.
208  *
209  * @param ctx The ctx containing include paths
210  * @param stmt The include statement, specifying the file name to look for.
211  * @param file_type Type of file (FILE_TYPE_KEYCODES, etc.)
212  * @param file_rtrn Returns the key map to be used.
213  * @param merge_rtrn Always returns stmt->merge.
214  *
215  * @return true on success or false otherwise.
216  */
217 bool
218 ProcessIncludeFile(struct xkb_context *ctx,
219                    IncludeStmt * stmt,
220                    enum xkb_file_type file_type,
221                    XkbFile ** file_rtrn, enum merge_mode *merge_rtrn)
222 {
223     FILE *file;
224     XkbFile *rtrn, *mapToUse, *next;
225
226     file = XkbFindFileInPath(ctx, stmt->file, file_type, NULL);
227     if (file == NULL) {
228         log_err(ctx, "Can't find file \"%s\" for %s include\n", stmt->file,
229                 XkbDirectoryForInclude(file_type));
230         return false;
231     }
232
233     if (!XKBParseFile(ctx, file, stmt->file, &rtrn)) {
234         log_err(ctx, "Error interpreting include file \"%s\"\n", stmt->file);
235         fclose(file);
236         return false;
237     }
238     fclose(file);
239
240     mapToUse = rtrn;
241     if (stmt->map != NULL) {
242         while (mapToUse)
243         {
244             next = (XkbFile *) mapToUse->common.next;
245             mapToUse->common.next = NULL;
246             if (streq(mapToUse->name, stmt->map) &&
247                 mapToUse->file_type == file_type) {
248                 FreeXKBFile(next);
249                 break;
250             }
251             else {
252                 FreeXKBFile(mapToUse);
253             }
254             mapToUse = next;
255         }
256         if (!mapToUse) {
257             log_err(ctx, "No %s named \"%s\" in the include file \"%s\"\n",
258                     FileTypeText(file_type), stmt->map, stmt->file);
259             return false;
260         }
261     }
262     else if (rtrn->common.next) {
263         log_lvl(ctx, 5,
264                 "No map in include statement, but \"%s\" contains several; "
265                 "Using first defined map, \"%s\"\n",
266                 stmt->file, rtrn->name);
267     }
268     if (mapToUse->file_type != file_type) {
269         log_err(ctx,
270                 "Include file wrong type (expected %s, got %s); "
271                 "Include file \"%s\" ignored\n",
272                 FileTypeText(file_type), FileTypeText(mapToUse->file_type),
273                 stmt->file);
274         return false;
275     }
276     /* FIXME: we have to check recursive includes here (or somewhere) */
277
278     *file_rtrn = mapToUse;
279     *merge_rtrn = stmt->merge;
280     return true;
281 }