Move include path from X11/extensions/ to xkbcommon/
[platform/upstream/libxkbcommon.git] / src / xkbcomp / xkbpath.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 #define DEBUG_VAR debugFlags
28 #include "utils.h"
29 #include <stdlib.h>
30 #include "xkbpath.h"
31 #include "xkbcommon/xkbcommon.h"
32 #include "XKBcommonint.h"
33
34 #ifndef DFLT_XKB_CONFIG_ROOT
35 #define DFLT_XKB_CONFIG_ROOT    "/usr/lib/X11/xkb"
36 #endif
37
38 #ifndef PATH_MAX
39 #define PATH_MAX 1024
40 #endif
41
42 /* initial szPath */
43 #define PATH_CHUNK      8
44
45 static Bool noDefaultPath = False;
46 /* number of entries allocated for includePath */
47 static int szPath;
48 /* number of actual entries in includePath */
49 static int nPathEntries;
50 /* Holds all directories we might be including data from */
51 static char **includePath = NULL;
52
53 /**
54  * Extract the first token from an include statement.
55  * @param str_inout Input statement, modified in-place. Can be passed in
56  * repeatedly. If str_inout is NULL, the parsing has completed.
57  * @param file_rtrn Set to the include file to be used.
58  * @param map_rtrn Set to whatever comes after ), if any.
59  * @param nextop_rtrn Set to the next operation in the complete statement.
60  * @param extra_data Set to the string between ( and ), if any.
61  *
62  * @return True if parsing was succcessful, False for an illegal string.
63  *
64  * Example: "evdev+aliases(qwerty)"
65  *      str_inout = aliases(qwerty)
66  *      nextop_retrn = +
67  *      extra_data = NULL
68  *      file_rtrn = evdev
69  *      map_rtrn = NULL
70  *
71  * 2nd run with "aliases(qwerty)"
72  *      str_inout = NULL
73  *      file_rtrn = aliases
74  *      map_rtrn = qwerty
75  *      extra_data = NULL
76  *      nextop_retrn = ""
77  *
78  */
79 Bool
80 XkbParseIncludeMap(char **str_inout, char **file_rtrn, char **map_rtrn,
81                    char *nextop_rtrn, char **extra_data)
82 {
83     char *tmp, *str, *next;
84
85     str = *str_inout;
86     if ((*str == '+') || (*str == '|'))
87     {
88         *file_rtrn = *map_rtrn = NULL;
89         *nextop_rtrn = *str;
90         next = str + 1;
91     }
92     else if (*str == '%')
93     {
94         *file_rtrn = *map_rtrn = NULL;
95         *nextop_rtrn = str[1];
96         next = str + 2;
97     }
98     else
99     {
100         /* search for tokens inside the string */
101         next = strpbrk(str, "|+");
102         if (next)
103         {
104             /* set nextop_rtrn to \0, next to next character */
105             *nextop_rtrn = *next;
106             *next++ = '\0';
107         }
108         else
109         {
110             *nextop_rtrn = '\0';
111             next = NULL;
112         }
113         /* search for :, store result in extra_data */
114         tmp = strchr(str, ':');
115         if (tmp != NULL)
116         {
117             *tmp++ = '\0';
118             *extra_data = _XkbDupString(tmp);
119         }
120         else
121         {
122             *extra_data = NULL;
123         }
124         tmp = strchr(str, '(');
125         if (tmp == NULL)
126         {
127             *file_rtrn = _XkbDupString(str);
128             *map_rtrn = NULL;
129         }
130         else if (str[0] == '(')
131         {
132             free(*extra_data);
133             return False;
134         }
135         else
136         {
137             *tmp++ = '\0';
138             *file_rtrn = _XkbDupString(str);
139             str = tmp;
140             tmp = strchr(str, ')');
141             if ((tmp == NULL) || (tmp[1] != '\0'))
142             {
143                 free(*file_rtrn);
144                 free(*extra_data);
145                 return False;
146             }
147             *tmp++ = '\0';
148             *map_rtrn = _XkbDupString(str);
149         }
150     }
151     if (*nextop_rtrn == '\0')
152         *str_inout = NULL;
153     else if ((*nextop_rtrn == '|') || (*nextop_rtrn == '+'))
154         *str_inout = next;
155     else
156         return False;
157     return True;
158 }
159
160 static void
161 XkbAddDefaultDirectoriesToPath(void);
162
163 /**
164  * Init memory for include paths.
165  */
166 static Bool
167 XkbInitIncludePath(void)
168 {
169     if (includePath)
170         return True;
171
172     szPath = PATH_CHUNK;
173     includePath = (char **) calloc(szPath, sizeof(char *));
174     if (!includePath)
175         return False;
176
177     XkbAddDefaultDirectoriesToPath();
178     return True;
179 }
180
181 /**
182  * Remove all entries from the global includePath.
183  */
184 static void
185 XkbClearIncludePath(void)
186 {
187     register int i;
188
189     if (szPath > 0)
190     {
191         for (i = 0; i < nPathEntries; i++)
192         {
193             if (includePath[i] != NULL)
194             {
195                 free(includePath[i]);
196                 includePath[i] = NULL;
197             }
198         }
199         nPathEntries = 0;
200     }
201     noDefaultPath = True;
202     return;
203 }
204
205 /**
206  * Add the given path to the global includePath variable.
207  * If dir is NULL, the includePath is emptied.
208  */
209 static Bool
210 XkbAddDirectoryToPath(const char *dir)
211 {
212     int len;
213
214     if (!XkbInitIncludePath())
215         return False;
216
217     if ((dir == NULL) || (dir[0] == '\0'))
218     {
219         XkbClearIncludePath();
220         return True;
221     }
222 #ifdef __UNIXOS2__
223     dir = (char *) __XOS2RedirRoot(dir);
224 #endif
225     len = strlen(dir);
226     if (len + 2 >= PATH_MAX)
227     {                           /* allow for '/' and at least one character */
228         ERROR("Path entry (%s) too long (maxiumum length is %d)\n",
229                dir, PATH_MAX - 3);
230         return False;
231     }
232     if (nPathEntries >= szPath)
233     {
234         szPath += PATH_CHUNK;
235         includePath = (char **) realloc(includePath, szPath * sizeof(char *));
236         if (includePath == NULL)
237         {
238             WSGO("Allocation failed (includePath)\n");
239             return False;
240         }
241     }
242     includePath[nPathEntries] = strdup(dir);
243     if (includePath[nPathEntries] == NULL)
244     {
245         WSGO("Allocation failed (includePath[%d])\n", nPathEntries);
246         return False;
247     }
248     nPathEntries++;
249     return True;
250 }
251
252 static void
253 XkbAddDefaultDirectoriesToPath(void)
254 {
255     if (!XkbInitIncludePath())
256         return;
257     if (noDefaultPath)
258         return;
259     XkbAddDirectoryToPath(DFLT_XKB_CONFIG_ROOT);
260 }
261
262 /***====================================================================***/
263
264 /**
265  * Return the xkb directory based on the type.
266  * Do not free the memory returned by this function.
267  */
268 char *
269 XkbDirectoryForInclude(unsigned type)
270 {
271     static char buf[32];
272
273     switch (type)
274     {
275     case XkmSemanticsFile:
276         strcpy(buf, "semantics");
277         break;
278     case XkmLayoutFile:
279         strcpy(buf, "layout");
280         break;
281     case XkmKeymapFile:
282         strcpy(buf, "keymap");
283         break;
284     case XkmKeyNamesIndex:
285         strcpy(buf, "keycodes");
286         break;
287     case XkmTypesIndex:
288         strcpy(buf, "types");
289         break;
290     case XkmSymbolsIndex:
291         strcpy(buf, "symbols");
292         break;
293     case XkmCompatMapIndex:
294         strcpy(buf, "compat");
295         break;
296     case XkmGeometryFile:
297     case XkmGeometryIndex:
298         strcpy(buf, "geometry");
299         break;
300     case XkmRulesFile:
301         strcpy(buf, "rules");
302         break;
303     default:
304         strcpy(buf, "");
305         break;
306     }
307     return buf;
308 }
309
310 /***====================================================================***/
311
312 typedef struct _FileCacheEntry
313 {
314     char *name;
315     unsigned type;
316     char *path;
317     void *data;
318     struct _FileCacheEntry *next;
319 } FileCacheEntry;
320 static FileCacheEntry *fileCache;
321
322 /**
323  * Add the file with the given name to the internal cache to avoid opening and
324  * parsing the file multiple times. If a cache entry for the same name + type
325  * is already present, the entry is overwritten and the data belonging to the
326  * previous entry is returned.
327  *
328  * @parameter name The name of the file (e.g. evdev).
329  * @parameter type Type of the file (XkbTypesIdx, ... or XkbSemanticsFile, ...)
330  * @parameter path The full path to the file.
331  * @parameter data Already parsed data.
332  *
333  * @return The data from the overwritten file or NULL.
334  */
335 void *
336 XkbAddFileToCache(char *name, unsigned type, char *path, void *data)
337 {
338     FileCacheEntry *entry;
339
340     for (entry = fileCache; entry != NULL; entry = entry->next)
341     {
342         if ((type == entry->type) && (uStringEqual(name, entry->name)))
343         {
344             void *old = entry->data;
345             WSGO("Replacing file cache entry (%s/%d)\n", name, type);
346             entry->path = path;
347             entry->data = data;
348             return old;
349         }
350     }
351     entry = uTypedAlloc(FileCacheEntry);
352     if (entry != NULL)
353     {
354         entry->name = name;
355         entry->type = type;
356         entry->path = path;
357         entry->data = data;
358         entry->next = fileCache;
359         fileCache = entry;
360     }
361     return NULL;
362 }
363
364 /**
365  * Search for the given name + type in the cache.
366  *
367  * @parameter name The name of the file (e.g. evdev).
368  * @parameter type Type of the file (XkbTypesIdx, ... or XkbSemanticsFile, ...)
369  * @parameter pathRtrn Set to the full path of the given entry.
370  *
371  * @return the data from the cache entry or NULL if no matching entry was found.
372  */
373 void *
374 XkbFindFileInCache(char *name, unsigned type, char **pathRtrn)
375 {
376     FileCacheEntry *entry;
377
378     for (entry = fileCache; entry != NULL; entry = entry->next)
379     {
380         if ((type == entry->type) && (uStringEqual(name, entry->name)))
381         {
382             *pathRtrn = entry->path;
383             return entry->data;
384         }
385     }
386     return NULL;
387 }
388
389 /***====================================================================***/
390
391 /**
392  * Search for the given file name in the include directories.
393  *
394  * @param type one of XkbTypesIndex, XkbCompatMapIndex, ..., or
395  * XkbSemanticsFile, XkmKeymapFile, ...
396  * @param pathReturn is set to the full path of the file if found.
397  *
398  * @return an FD to the file or NULL. If NULL is returned, the value of
399  * pathRtrn is undefined.
400  */
401 FILE *
402 XkbFindFileInPath(char *name, unsigned type, char **pathRtrn)
403 {
404     register int i;
405     FILE *file = NULL;
406     int nameLen, typeLen, pathLen;
407     char buf[PATH_MAX], *typeDir;
408
409     if (!XkbInitIncludePath())
410         return NULL;
411
412     typeDir = XkbDirectoryForInclude(type);
413     nameLen = strlen(name);
414     typeLen = strlen(typeDir);
415     for (i = 0; i < nPathEntries; i++)
416     {
417         pathLen = strlen(includePath[i]);
418         if (typeLen < 1)
419             continue;
420
421         if ((nameLen + typeLen + pathLen + 2) >= PATH_MAX)
422         {
423             ERROR("File name (%s/%s/%s) too long\n", includePath[i],
424                    typeDir, name);
425             ACTION("Ignored\n");
426             continue;
427         }
428         snprintf(buf, sizeof(buf), "%s/%s/%s", includePath[i], typeDir, name);
429         file = fopen(buf, "r");
430         if (file != NULL)
431             break;
432     }
433
434     if ((file != NULL) && (pathRtrn != NULL))
435     {
436         *pathRtrn = (char *) calloc(strlen(buf) + 1, sizeof(char));
437         if (*pathRtrn != NULL)
438             strcpy(*pathRtrn, buf);
439     }
440     return file;
441 }