1/************************************************************
2 Copyright 1996 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
28Copyright 1988, 1998  The Open Group
29
30Permission to use, copy, modify, distribute, and sell this software and its
31documentation for any purpose is hereby granted without fee, provided that
32the above copyright notice appear in all copies and that both that
33copyright notice and this permission notice appear in supporting
34documentation.
35
36The above copyright notice and this permission notice shall be included in
37all copies or substantial portions of the Software.
38
39THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
40IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
41FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
42OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
43AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
44CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
45
46Except as contained in this notice, the name of The Open Group shall not be
47used in advertising or otherwise to promote the sale, use or other dealings
48in this Software without prior written authorization from The Open Group.
49
50
51Copyright 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
52
53                        All Rights Reserved
54
55Permission to use, copy, modify, and distribute this software and its
56documentation for any purpose and without fee is hereby granted,
57provided that the above copyright notice appear in all copies and that
58both that copyright notice and this permission notice appear in
59supporting documentation, and that the name of Digital not be
60used in advertising or publicity pertaining to distribution of the
61software without specific, written prior permission.
62
63DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
64ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
65DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
66ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
67WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
68ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
69SOFTWARE.
70
71******************************************************************/
72
73#include <stdio.h>
74#include <ctype.h>
75#include <sys/types.h>
76#include <sys/stat.h>
77#include <X11/keysym.h>
78
79#define	DEBUG_VAR listingDebug
80#include "xkbcomp.h"
81#include <stdlib.h>
82
83#ifdef _POSIX_SOURCE
84# include <limits.h>
85#else
86# define _POSIX_SOURCE
87# include <limits.h>
88# undef _POSIX_SOURCE
89#endif
90
91#ifndef PATH_MAX
92#ifdef WIN32
93#define PATH_MAX 512
94#else
95#include <sys/param.h>
96#endif
97#ifndef PATH_MAX
98#ifdef MAXPATHLEN
99#define PATH_MAX MAXPATHLEN
100#else
101#define PATH_MAX 1024
102#endif
103#endif
104#endif
105
106#ifdef WIN32
107# define WIN32_LEAN_AND_MEAN
108# include <X11/Xwindows.h>
109# define FileName(file) file.cFileName
110# undef TEXT
111# undef ALTERNATE
112#else
113# include <dirent.h>
114# define FileName(file) file->d_name
115#endif
116
117#include "xkbpath.h"
118#include "parseutils.h"
119#include "misc.h"
120#include "tokens.h"
121#include <X11/extensions/XKBgeom.h>
122
123#define	lowbit(x)	((x) & (-(x)))
124
125#ifdef DEBUG
126unsigned int listingDebug;
127#endif
128
129static int szListing = 0;
130static int nListed = 0;
131static int nFilesListed = 0;
132
133typedef struct _Listing
134{
135    char *file;
136    char *map;
137} Listing;
138
139static int szMapOnly;
140static int nMapOnly;
141static char **mapOnly;
142
143static Listing *list = NULL;
144
145/***====================================================================***/
146
147int
148AddMapOnly(char *map)
149{
150    if (nMapOnly >= szMapOnly)
151    {
152        if (szMapOnly < 1)
153            szMapOnly = 5;
154        else
155            szMapOnly *= 2;
156        mapOnly = reallocarray(list, szMapOnly, sizeof(char *));
157        if (!mapOnly)
158        {
159            WSGO("Couldn't allocate list of maps\n");
160            return 0;
161        }
162    }
163    mapOnly[nMapOnly++] = map;
164    return 1;
165}
166
167static int
168AddListing(char *file, char *map)
169{
170    if (nListed >= szListing)
171    {
172        if (szListing < 1)
173            szListing = 10;
174        else
175            szListing *= 2;
176        list = reallocarray(list, szListing, sizeof(Listing));
177        if (!list)
178        {
179            WSGO("Couldn't allocate list of files and maps\n");
180            ACTION("Exiting\n");
181            exit(1);
182        }
183    }
184
185    list[nListed].file = file;
186    list[nListed].map = map;
187    nListed++;
188    if (file != NULL)
189        nFilesListed++;
190    return 1;
191}
192
193/***====================================================================***/
194
195static void
196ListFile(FILE *outFile, const char *fileName, XkbFile *map)
197{
198    unsigned flags;
199    const char *mapName;
200
201    flags = map->flags;
202    if ((flags & XkbLC_Hidden) && (!(verboseLevel & WantHiddenMaps)))
203        return;
204    if ((flags & XkbLC_Partial) && (!(verboseLevel & WantPartialMaps)))
205        return;
206    if (verboseLevel & WantLongListing)
207    {
208        fprintf(outFile, (flags & XkbLC_Hidden) ? "h" : "-");
209        fprintf(outFile, (flags & XkbLC_Default) ? "d" : "-");
210        fprintf(outFile, (flags & XkbLC_Partial) ? "p" : "-");
211        fprintf(outFile, "----- ");
212        if (map->type == XkmSymbolsIndex)
213        {
214            fprintf(outFile, (flags & XkbLC_AlphanumericKeys) ? "a" : "-");
215            fprintf(outFile, (flags & XkbLC_ModifierKeys) ? "m" : "-");
216            fprintf(outFile, (flags & XkbLC_KeypadKeys) ? "k" : "-");
217            fprintf(outFile, (flags & XkbLC_FunctionKeys) ? "f" : "-");
218            fprintf(outFile, (flags & XkbLC_AlternateGroup) ? "g" : "-");
219            fprintf(outFile, "--- ");
220        }
221        else
222            fprintf(outFile, "-------- ");
223    }
224    mapName = map->name;
225    if ((!(verboseLevel & WantFullNames)) && ((flags & XkbLC_Default) != 0))
226        mapName = NULL;
227    if (dirsToStrip > 0)
228    {
229        const char *tmp, *last;
230        int i;
231        for (i = 0, tmp = last = fileName; (i < dirsToStrip) && tmp; i++)
232        {
233            last = tmp;
234            tmp = strchr(tmp, '/');
235            if (tmp != NULL)
236                tmp++;
237        }
238        fileName = (tmp ? tmp : last);
239    }
240    if (mapName)
241        fprintf(outFile, "%s(%s)\n", fileName, mapName);
242    else
243        fprintf(outFile, "%s\n", fileName);
244    return;
245}
246
247/***====================================================================***/
248
249static int
250AddDirectory(char *head, char *ptrn, char *rest, char *map)
251{
252#ifdef WIN32
253    HANDLE dirh;
254    WIN32_FIND_DATA file;
255#else
256    DIR *dirp;
257    struct dirent *file;
258#endif
259    int nMatch;
260
261    if (map == NULL)
262    {
263        char *tmp = ptrn;
264        if ((rest == NULL) && (ptrn != NULL) && (strchr(ptrn, '/') == NULL))
265        {
266            tmp = ptrn;
267            map = strchr(ptrn, '(');
268        }
269        else if ((rest == NULL) && (ptrn == NULL) &&
270                 (head != NULL) && (strchr(head, '/') == NULL))
271        {
272            tmp = head;
273            map = strchr(head, '(');
274        }
275        if (map != NULL)
276        {
277            tmp = strchr(tmp, ')');
278            if ((tmp == NULL) || (tmp[1] != '\0'))
279            {
280                ERROR("File and map must have the format file(map)\n");
281                return 0;
282            }
283            *map = '\0';
284            map++;
285            *tmp = '\0';
286        }
287    }
288#ifdef WIN32
289    if ((dirh = FindFirstFile("*.*", &file)) == INVALID_HANDLE_VALUE)
290        return 0;
291#else
292    if ((dirp = opendir((head ? head : "."))) == NULL)
293        return 0;
294#endif
295    nMatch = 0;
296#ifdef WIN32
297    do
298#else
299    while ((file = readdir(dirp)) != NULL)
300#endif
301    {
302        char *tmp, *filename;
303        struct stat sbuf;
304
305        filename = FileName(file);
306        if (!filename || filename[0] == '.')
307            continue;
308        if (ptrn && (!XkbNameMatchesPattern(filename, ptrn)))
309            continue;
310#ifdef HAVE_ASPRINTF
311        if (asprintf(&tmp, "%s%s%s",
312                     (head ? head : ""), (head ? "/" : ""), filename) < 0)
313            continue;
314#else
315        size_t tmpsize = (head ? strlen(head) : 0) + strlen(filename) + 2;
316        tmp = malloc(tmpsize);
317        if (!tmp)
318            continue;
319        snprintf(tmp, tmpsize, "%s%s%s",
320                 (head ? head : ""), (head ? "/" : ""), filename);
321#endif
322        if (stat(tmp, &sbuf) < 0)
323        {
324            free(tmp);
325            continue;
326        }
327        if (((rest != NULL) && (!S_ISDIR(sbuf.st_mode))) ||
328            ((map != NULL) && (S_ISDIR(sbuf.st_mode))))
329        {
330            free(tmp);
331            continue;
332        }
333        if (S_ISDIR(sbuf.st_mode))
334        {
335            if ((rest != NULL) || (verboseLevel & ListRecursive))
336                nMatch += AddDirectory(tmp, rest, NULL, map);
337        }
338        else
339            nMatch += AddListing(tmp, map);
340    }
341#ifdef WIN32
342    while (FindNextFile(dirh, &file));
343#endif
344    return nMatch;
345}
346
347/***====================================================================***/
348
349Bool
350AddMatchingFiles(char *head_in)
351{
352    char *str, *head, *ptrn, *rest = NULL;
353
354    if (head_in == NULL)
355        return 0;
356    ptrn = NULL;
357    for (str = head_in; (*str != '\0') && (*str != '?') && (*str != '*');
358         str++)
359    {
360        if ((str != head_in) && (*str == '/'))
361            ptrn = str;
362    }
363    if (*str == '\0')
364    {                           /* no wildcards */
365        head = head_in;
366        ptrn = NULL;
367        rest = NULL;
368    }
369    else if (ptrn == NULL)
370    {                           /* no slash before the first wildcard */
371        head = NULL;
372        ptrn = head_in;
373    }
374    else
375    {                           /* slash followed by wildcard */
376        head = head_in;
377        *ptrn = '\0';
378        ptrn++;
379    }
380    if (ptrn)
381    {
382        rest = strchr(ptrn, '/');
383        if (rest != NULL)
384        {
385            *rest = '\0';
386            rest++;
387        }
388    }
389    if (((rest && ptrn)
390         && ((strchr(ptrn, '(') != NULL) || (strchr(ptrn, ')') != NULL)))
391        || (head
392            && ((strchr(head, '(') != NULL) || (strchr(head, ')') != NULL))))
393    {
394        ERROR("Files/maps to list must have the form file(map)\n");
395        ACTION("Illegal specifier ignored\n");
396        return 0;
397    }
398    return AddDirectory(head, ptrn, rest, NULL);
399}
400
401/***====================================================================***/
402
403static Bool
404MapMatches(char *mapToConsider, char *ptrn)
405{
406    if (ptrn != NULL)
407        return XkbNameMatchesPattern(mapToConsider, ptrn);
408    if (nMapOnly < 1)
409        return True;
410    for (int i = 0; i < nMapOnly; i++)
411    {
412        if (XkbNameMatchesPattern(mapToConsider, mapOnly[i]))
413            return True;
414    }
415    return False;
416}
417
418int
419GenerateListing(const char *out_name)
420{
421    FILE *outFile;
422    XkbFile *rtrn;
423
424    if (nFilesListed < 1)
425    {
426        ERROR("Must specify at least one file or pattern to list\n");
427        return 0;
428    }
429    if ((!out_name) || ((out_name[0] == '-') && (out_name[1] == '\0')))
430        outFile = stdout;
431    else if ((outFile = fopen(out_name, "w")) == NULL)
432    {
433        ERROR("Cannot open \"%s\" to write keyboard description\n",
434               out_name);
435        ACTION("Exiting\n");
436        return 0;
437    }
438#ifdef DEBUG
439    if (warningLevel > 9)
440        fprintf(stderr, "should list:\n");
441#endif
442    for (int i = 0; i < nListed; i++)
443    {
444        unsigned oldWarningLevel;
445#ifdef DEBUG
446        if (warningLevel > 9)
447        {
448            fprintf(stderr, "%s(%s)\n",
449                    (list[i].file ? list[i].file : "*"),
450                    (list[i].map ? list[i].map : "*"));
451        }
452#endif
453        oldWarningLevel = warningLevel;
454        warningLevel = 0;
455        if (list[i].file)
456        {
457            FILE *inputFile;
458            struct stat sbuf;
459
460            if (stat(list[i].file, &sbuf) < 0)
461            {
462                if (oldWarningLevel > 5)
463                    WARN("Couldn't open \"%s\"\n", list[i].file);
464                continue;
465            }
466            if (S_ISDIR(sbuf.st_mode))
467            {
468                if (verboseLevel & ListRecursive)
469                    AddDirectory(list[i].file, NULL, NULL, NULL);
470                continue;
471            }
472
473            inputFile = fopen(list[i].file, "r");
474            if (!inputFile)
475            {
476                if (oldWarningLevel > 5)
477                    WARN("Couldn't open \"%s\"\n", list[i].file);
478                continue;
479            }
480            setScanState(list[i].file, 1);
481            if (XKBParseFile(inputFile, &rtrn) && (rtrn != NULL))
482            {
483                char *mapName = list[i].map;
484                XkbFile *mapToUse = rtrn;
485                for (; mapToUse; mapToUse = (XkbFile *) mapToUse->common.next)
486                {
487                    if (!MapMatches(mapToUse->name, mapName))
488                        continue;
489                    ListFile(outFile, list[i].file, mapToUse);
490                }
491            }
492            fclose(inputFile);
493        }
494        warningLevel = oldWarningLevel;
495    }
496    if (outFile != stdout)
497    {
498        fclose(outFile);
499    }
500    return 1;
501}
502