listing.c revision 6930ead5
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 "utils.h"
74#include <stdio.h>
75#include <ctype.h>
76#include <sys/types.h>
77#include <sys/stat.h>
78#include <X11/keysym.h>
79
80#define	DEBUG_VAR listingDebug
81#include "xkbcomp.h"
82#include <stdlib.h>
83
84#ifdef _POSIX_SOURCE
85# include <limits.h>
86#else
87# define _POSIX_SOURCE
88# include <limits.h>
89# undef _POSIX_SOURCE
90#endif
91
92#ifndef PATH_MAX
93#ifdef WIN32
94#define PATH_MAX 512
95#else
96#include <sys/param.h>
97#endif
98#ifndef PATH_MAX
99#ifdef MAXPATHLEN
100#define PATH_MAX MAXPATHLEN
101#else
102#define PATH_MAX 1024
103#endif
104#endif
105#endif
106
107#ifdef WIN32
108# define WIN32_LEAN_AND_MEAN
109# include <X11/Xwindows.h>
110# define FileName(file) file.cFileName
111# undef TEXT
112# undef ALTERNATE
113#else
114# include <dirent.h>
115# define FileName(file) file->d_name
116#endif
117
118#include "xkbpath.h"
119#include "parseutils.h"
120#include "misc.h"
121#include "tokens.h"
122#include <X11/extensions/XKBgeom.h>
123
124#define	lowbit(x)	((x) & (-(x)))
125
126#ifdef DEBUG
127unsigned int listingDebug;
128#endif
129
130static int szListing = 0;
131static int nListed = 0;
132static int nFilesListed = 0;
133
134typedef struct _Listing
135{
136    char *file;
137    char *map;
138} Listing;
139
140static int szMapOnly;
141static int nMapOnly;
142static char **mapOnly;
143
144static Listing *list = NULL;
145
146/***====================================================================***/
147
148int
149AddMapOnly(char *map)
150{
151    if (nMapOnly >= szMapOnly)
152    {
153        if (szMapOnly < 1)
154            szMapOnly = 5;
155        else
156            szMapOnly *= 2;
157        mapOnly = reallocarray(list, szMapOnly, sizeof(char *));
158        if (!mapOnly)
159        {
160            WSGO("Couldn't allocate list of maps\n");
161            return 0;
162        }
163    }
164    mapOnly[nMapOnly++] = map;
165    return 1;
166}
167
168static int
169AddListing(char *file, char *map)
170{
171    if (nListed >= szListing)
172    {
173        if (szListing < 1)
174            szListing = 10;
175        else
176            szListing *= 2;
177        list = reallocarray(list, szListing, sizeof(Listing));
178        if (!list)
179        {
180            WSGO("Couldn't allocate list of files and maps\n");
181            ACTION("Exiting\n");
182            exit(1);
183        }
184    }
185
186    list[nListed].file = file;
187    list[nListed].map = map;
188    nListed++;
189    if (file != NULL)
190        nFilesListed++;
191    return 1;
192}
193
194/***====================================================================***/
195
196static void
197ListFile(FILE *outFile, const char *fileName, XkbFile *map)
198{
199    unsigned flags;
200    const char *mapName;
201
202    flags = map->flags;
203    if ((flags & XkbLC_Hidden) && (!(verboseLevel & WantHiddenMaps)))
204        return;
205    if ((flags & XkbLC_Partial) && (!(verboseLevel & WantPartialMaps)))
206        return;
207    if (verboseLevel & WantLongListing)
208    {
209        fprintf(outFile, (flags & XkbLC_Hidden) ? "h" : "-");
210        fprintf(outFile, (flags & XkbLC_Default) ? "d" : "-");
211        fprintf(outFile, (flags & XkbLC_Partial) ? "p" : "-");
212        fprintf(outFile, "----- ");
213        if (map->type == XkmSymbolsIndex)
214        {
215            fprintf(outFile, (flags & XkbLC_AlphanumericKeys) ? "a" : "-");
216            fprintf(outFile, (flags & XkbLC_ModifierKeys) ? "m" : "-");
217            fprintf(outFile, (flags & XkbLC_KeypadKeys) ? "k" : "-");
218            fprintf(outFile, (flags & XkbLC_FunctionKeys) ? "f" : "-");
219            fprintf(outFile, (flags & XkbLC_AlternateGroup) ? "g" : "-");
220            fprintf(outFile, "--- ");
221        }
222        else
223            fprintf(outFile, "-------- ");
224    }
225    mapName = map->name;
226    if ((!(verboseLevel & WantFullNames)) && ((flags & XkbLC_Default) != 0))
227        mapName = NULL;
228    if (dirsToStrip > 0)
229    {
230        const char *tmp, *last;
231        int i;
232        for (i = 0, tmp = last = fileName; (i < dirsToStrip) && tmp; i++)
233        {
234            last = tmp;
235            tmp = strchr(tmp, '/');
236            if (tmp != NULL)
237                tmp++;
238        }
239        fileName = (tmp ? tmp : last);
240    }
241    if (mapName)
242        fprintf(outFile, "%s(%s)\n", fileName, mapName);
243    else
244        fprintf(outFile, "%s\n", fileName);
245    return;
246}
247
248/***====================================================================***/
249
250static int
251AddDirectory(char *head, char *ptrn, char *rest, char *map)
252{
253#ifdef WIN32
254    HANDLE dirh;
255    WIN32_FIND_DATA file;
256#else
257    DIR *dirp;
258    struct dirent *file;
259#endif
260    int nMatch;
261
262    if (map == NULL)
263    {
264        char *tmp = ptrn;
265        if ((rest == NULL) && (ptrn != NULL) && (strchr(ptrn, '/') == NULL))
266        {
267            tmp = ptrn;
268            map = strchr(ptrn, '(');
269        }
270        else if ((rest == NULL) && (ptrn == NULL) &&
271                 (head != NULL) && (strchr(head, '/') == NULL))
272        {
273            tmp = head;
274            map = strchr(head, '(');
275        }
276        if (map != NULL)
277        {
278            tmp = strchr(tmp, ')');
279            if ((tmp == NULL) || (tmp[1] != '\0'))
280            {
281                ERROR("File and map must have the format file(map)\n");
282                return 0;
283            }
284            *map = '\0';
285            map++;
286            *tmp = '\0';
287        }
288    }
289#ifdef WIN32
290    if ((dirh = FindFirstFile("*.*", &file)) == INVALID_HANDLE_VALUE)
291        return 0;
292#else
293    if ((dirp = opendir((head ? head : "."))) == NULL)
294        return 0;
295#endif
296    nMatch = 0;
297#ifdef WIN32
298    do
299#else
300    while ((file = readdir(dirp)) != NULL)
301#endif
302    {
303        char *tmp, *filename;
304        struct stat sbuf;
305
306        filename = FileName(file);
307        if (!filename || filename[0] == '.')
308            continue;
309        if (ptrn && (!XkbNameMatchesPattern(filename, ptrn)))
310            continue;
311#ifdef HAVE_ASPRINTF
312        if (asprintf(&tmp, "%s%s%s",
313                     (head ? head : ""), (head ? "/" : ""), filename) < 0)
314            continue;
315#else
316        size_t tmpsize = (head ? strlen(head) : 0) + strlen(filename) + 2;
317        tmp = malloc(tmpsize);
318        if (!tmp)
319            continue;
320        snprintf(tmp, tmpsize, "%s%s%s",
321                 (head ? head : ""), (head ? "/" : ""), filename);
322#endif
323        if (stat(tmp, &sbuf) < 0)
324        {
325            free(tmp);
326            continue;
327        }
328        if (((rest != NULL) && (!S_ISDIR(sbuf.st_mode))) ||
329            ((map != NULL) && (S_ISDIR(sbuf.st_mode))))
330        {
331            free(tmp);
332            continue;
333        }
334        if (S_ISDIR(sbuf.st_mode))
335        {
336            if ((rest != NULL) || (verboseLevel & ListRecursive))
337                nMatch += AddDirectory(tmp, rest, NULL, map);
338        }
339        else
340            nMatch += AddListing(tmp, map);
341    }
342#ifdef WIN32
343    while (FindNextFile(dirh, &file));
344#endif
345    return nMatch;
346}
347
348/***====================================================================***/
349
350Bool
351AddMatchingFiles(char *head_in)
352{
353    char *str, *head, *ptrn, *rest = NULL;
354
355    if (head_in == NULL)
356        return 0;
357    ptrn = NULL;
358    for (str = head_in; (*str != '\0') && (*str != '?') && (*str != '*');
359         str++)
360    {
361        if ((str != head_in) && (*str == '/'))
362            ptrn = str;
363    }
364    if (*str == '\0')
365    {                           /* no wildcards */
366        head = head_in;
367        ptrn = NULL;
368        rest = NULL;
369    }
370    else if (ptrn == NULL)
371    {                           /* no slash before the first wildcard */
372        head = NULL;
373        ptrn = head_in;
374    }
375    else
376    {                           /* slash followed by wildcard */
377        head = head_in;
378        *ptrn = '\0';
379        ptrn++;
380    }
381    if (ptrn)
382    {
383        rest = strchr(ptrn, '/');
384        if (rest != NULL)
385        {
386            *rest = '\0';
387            rest++;
388        }
389    }
390    if (((rest && ptrn)
391         && ((strchr(ptrn, '(') != NULL) || (strchr(ptrn, ')') != NULL)))
392        || (head
393            && ((strchr(head, '(') != NULL) || (strchr(head, ')') != NULL))))
394    {
395        ERROR("Files/maps to list must have the form file(map)\n");
396        ACTION("Illegal specifier ignored\n");
397        return 0;
398    }
399    return AddDirectory(head, ptrn, rest, NULL);
400}
401
402/***====================================================================***/
403
404static Bool
405MapMatches(char *mapToConsider, char *ptrn)
406{
407    if (ptrn != NULL)
408        return XkbNameMatchesPattern(mapToConsider, ptrn);
409    if (nMapOnly < 1)
410        return True;
411    for (int i = 0; i < nMapOnly; i++)
412    {
413        if (XkbNameMatchesPattern(mapToConsider, mapOnly[i]))
414            return True;
415    }
416    return False;
417}
418
419int
420GenerateListing(const char *out_name)
421{
422    FILE *outFile;
423    XkbFile *rtrn;
424
425    if (nFilesListed < 1)
426    {
427        ERROR("Must specify at least one file or pattern to list\n");
428        return 0;
429    }
430    if ((!out_name) || ((out_name[0] == '-') && (out_name[1] == '\0')))
431        outFile = stdout;
432    else if ((outFile = fopen(out_name, "w")) == NULL)
433    {
434        ERROR("Cannot open \"%s\" to write keyboard description\n",
435               out_name);
436        ACTION("Exiting\n");
437        return 0;
438    }
439#ifdef DEBUG
440    if (warningLevel > 9)
441        fprintf(stderr, "should list:\n");
442#endif
443    for (int i = 0; i < nListed; i++)
444    {
445        unsigned oldWarningLevel;
446#ifdef DEBUG
447        if (warningLevel > 9)
448        {
449            fprintf(stderr, "%s(%s)\n",
450                    (list[i].file ? list[i].file : "*"),
451                    (list[i].map ? list[i].map : "*"));
452        }
453#endif
454        oldWarningLevel = warningLevel;
455        warningLevel = 0;
456        if (list[i].file)
457        {
458            FILE *inputFile;
459            struct stat sbuf;
460
461            if (stat(list[i].file, &sbuf) < 0)
462            {
463                if (oldWarningLevel > 5)
464                    WARN("Couldn't open \"%s\"\n", list[i].file);
465                continue;
466            }
467            if (S_ISDIR(sbuf.st_mode))
468            {
469                if (verboseLevel & ListRecursive)
470                    AddDirectory(list[i].file, NULL, NULL, NULL);
471                continue;
472            }
473
474            inputFile = fopen(list[i].file, "r");
475            if (!inputFile)
476            {
477                if (oldWarningLevel > 5)
478                    WARN("Couldn't open \"%s\"\n", list[i].file);
479                continue;
480            }
481            setScanState(list[i].file, 1);
482            if (XKBParseFile(inputFile, &rtrn) && (rtrn != NULL))
483            {
484                char *mapName = list[i].map;
485                XkbFile *mapToUse = rtrn;
486                for (; mapToUse; mapToUse = (XkbFile *) mapToUse->common.next)
487                {
488                    if (!MapMatches(mapToUse->name, mapName))
489                        continue;
490                    ListFile(outFile, list[i].file, mapToUse);
491                }
492            }
493            fclose(inputFile);
494        }
495        warningLevel = oldWarningLevel;
496    }
497    if (outFile != stdout)
498    {
499        fclose(outFile);
500    }
501    return 1;
502}
503