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