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