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