1/************************************************************
2
3Author: Eamon Walsh <ewalsh@tycho.nsa.gov>
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7this permission notice appear in supporting documentation.  This permission
8notice shall be included in all copies or substantial portions of the
9Software.
10
11THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
14AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
15AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
16CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
18********************************************************/
19
20#ifdef HAVE_DIX_CONFIG_H
21#include <dix-config.h>
22#endif
23
24#include <stdlib.h>
25#include <string.h>
26#include <X11/X.h>
27#include <X11/Xproto.h>
28#include "resource.h"
29#include "registry.h"
30
31#define BASE_SIZE 16
32
33#ifdef X_REGISTRY_REQUEST
34#define CORE "X11"
35#define FILENAME SERVER_MISC_CONFIG_PATH "/protocol.txt"
36
37#define PROT_COMMENT '#'
38#define PROT_REQUEST 'R'
39#define PROT_EVENT 'V'
40#define PROT_ERROR 'E'
41
42static FILE *fh;
43
44static char ***requests, **events, **errors;
45static unsigned nmajor, *nminor, nevent, nerror;
46#endif
47
48#ifdef X_REGISTRY_RESOURCE
49static const char **resources;
50static unsigned nresource;
51#endif
52
53#if defined(X_REGISTRY_RESOURCE) || defined(X_REGISTRY_REQUEST)
54/*
55 * File parsing routines
56 */
57static int
58double_size(void *p, unsigned n, unsigned size)
59{
60    char **ptr = (char **) p;
61    unsigned s, f;
62
63    if (n) {
64        s = n * size;
65        n *= 2 * size;
66        f = n;
67    }
68    else {
69        s = 0;
70        n = f = BASE_SIZE * size;
71    }
72
73    *ptr = realloc(*ptr, n);
74    if (!*ptr) {
75        dixResetRegistry();
76        return FALSE;
77    }
78    memset(*ptr + s, 0, f - s);
79    return TRUE;
80}
81#endif
82
83#ifdef X_REGISTRY_REQUEST
84/*
85 * Request/event/error registry functions
86 */
87static void
88RegisterRequestName(unsigned major, unsigned minor, char *name)
89{
90    while (major >= nmajor) {
91        if (!double_size(&requests, nmajor, sizeof(char **)))
92            return;
93        if (!double_size(&nminor, nmajor, sizeof(unsigned)))
94            return;
95        nmajor = nmajor ? nmajor * 2 : BASE_SIZE;
96    }
97    while (minor >= nminor[major]) {
98        if (!double_size(requests + major, nminor[major], sizeof(char *)))
99            return;
100        nminor[major] = nminor[major] ? nminor[major] * 2 : BASE_SIZE;
101    }
102
103    free(requests[major][minor]);
104    requests[major][minor] = name;
105}
106
107static void
108RegisterEventName(unsigned event, char *name)
109{
110    while (event >= nevent) {
111        if (!double_size(&events, nevent, sizeof(char *)))
112            return;
113        nevent = nevent ? nevent * 2 : BASE_SIZE;
114    }
115
116    free(events[event]);
117    events[event] = name;
118}
119
120static void
121RegisterErrorName(unsigned error, char *name)
122{
123    while (error >= nerror) {
124        if (!double_size(&errors, nerror, sizeof(char *)))
125            return;
126        nerror = nerror ? nerror * 2 : BASE_SIZE;
127    }
128
129    free(errors[error]);
130    errors[error] = name;
131}
132
133void
134RegisterExtensionNames(ExtensionEntry * extEntry)
135{
136    char buf[256], *lineobj, *ptr;
137    unsigned offset;
138
139    if (fh == NULL)
140        return;
141
142    rewind(fh);
143
144    while (fgets(buf, sizeof(buf), fh)) {
145        lineobj = NULL;
146        ptr = strchr(buf, '\n');
147        if (ptr)
148            *ptr = 0;
149
150        /* Check for comments or empty lines */
151        switch (buf[0]) {
152        case PROT_REQUEST:
153        case PROT_EVENT:
154        case PROT_ERROR:
155            break;
156        case PROT_COMMENT:
157        case '\0':
158            continue;
159        default:
160            goto invalid;
161        }
162
163        /* Check for space character in the fifth position */
164        ptr = strchr(buf, ' ');
165        if (!ptr || ptr != buf + 4)
166            goto invalid;
167
168        /* Duplicate the string after the space */
169        lineobj = strdup(ptr + 1);
170        if (!lineobj)
171            continue;
172
173        /* Check for a colon somewhere on the line */
174        ptr = strchr(buf, ':');
175        if (!ptr)
176            goto invalid;
177
178        /* Compare the part before colon with the target extension name */
179        *ptr = 0;
180        if (strcmp(buf + 5, extEntry->name))
181            goto skip;
182
183        /* Get the opcode for the request, event, or error */
184        offset = strtol(buf + 1, &ptr, 10);
185        if (offset == 0 && ptr == buf + 1)
186            goto invalid;
187
188        /* Save the strdup result in the registry */
189        switch (buf[0]) {
190        case PROT_REQUEST:
191            if (extEntry->base)
192                RegisterRequestName(extEntry->base, offset, lineobj);
193            else
194                RegisterRequestName(offset, 0, lineobj);
195            continue;
196        case PROT_EVENT:
197            RegisterEventName(extEntry->eventBase + offset, lineobj);
198            continue;
199        case PROT_ERROR:
200            RegisterErrorName(extEntry->errorBase + offset, lineobj);
201            continue;
202        }
203
204 invalid:
205        LogMessage(X_WARNING, "Invalid line in " FILENAME ", skipping\n");
206 skip:
207        free(lineobj);
208    }
209}
210
211const char *
212LookupRequestName(int major, int minor)
213{
214    if (major >= nmajor)
215        return XREGISTRY_UNKNOWN;
216    if (minor >= nminor[major])
217        return XREGISTRY_UNKNOWN;
218
219    return requests[major][minor] ? requests[major][minor] : XREGISTRY_UNKNOWN;
220}
221
222const char *
223LookupMajorName(int major)
224{
225    if (major < 128) {
226        const char *retval;
227
228        if (major >= nmajor)
229            return XREGISTRY_UNKNOWN;
230        if (0 >= nminor[major])
231            return XREGISTRY_UNKNOWN;
232
233        retval = requests[major][0];
234        return retval ? retval + sizeof(CORE) : XREGISTRY_UNKNOWN;
235    }
236    else {
237        ExtensionEntry *extEntry = GetExtensionEntry(major);
238
239        return extEntry ? extEntry->name : XREGISTRY_UNKNOWN;
240    }
241}
242
243const char *
244LookupEventName(int event)
245{
246    event &= 127;
247    if (event >= nevent)
248        return XREGISTRY_UNKNOWN;
249
250    return events[event] ? events[event] : XREGISTRY_UNKNOWN;
251}
252
253const char *
254LookupErrorName(int error)
255{
256    if (error >= nerror)
257        return XREGISTRY_UNKNOWN;
258
259    return errors[error] ? errors[error] : XREGISTRY_UNKNOWN;
260}
261#endif /* X_REGISTRY_REQUEST */
262
263#ifdef X_REGISTRY_RESOURCE
264/*
265 * Resource registry functions
266 */
267
268void
269RegisterResourceName(RESTYPE resource, const char *name)
270{
271    resource &= TypeMask;
272
273    while (resource >= nresource) {
274        if (!double_size(&resources, nresource, sizeof(char *)))
275            return;
276        nresource = nresource ? nresource * 2 : BASE_SIZE;
277    }
278
279    resources[resource] = name;
280}
281
282const char *
283LookupResourceName(RESTYPE resource)
284{
285    resource &= TypeMask;
286    if (resource >= nresource)
287        return XREGISTRY_UNKNOWN;
288
289    return resources[resource] ? resources[resource] : XREGISTRY_UNKNOWN;
290}
291#endif /* X_REGISTRY_RESOURCE */
292
293void
294dixFreeRegistry(void)
295{
296#ifdef X_REGISTRY_REQUEST
297    /* Free all memory */
298    while (nmajor--) {
299        while (nminor[nmajor])
300            free(requests[nmajor][--nminor[nmajor]]);
301        free(requests[nmajor]);
302    }
303    free(requests);
304    free(nminor);
305
306    while (nevent--)
307        free(events[nevent]);
308    free(events);
309
310    while (nerror--)
311        free(errors[nerror]);
312    free(errors);
313    requests = NULL;
314    nminor = NULL;
315    events = NULL;
316    errors = NULL;
317    nmajor = nevent = nerror = 0;
318#endif
319
320#ifdef X_REGISTRY_RESOURCE
321    free(resources);
322
323    resources = NULL;
324    nresource = 0;
325#endif
326}
327
328void
329dixCloseRegistry(void)
330{
331#ifdef X_REGISTRY_REQUEST
332    if (fh) {
333	fclose(fh);
334        fh = NULL;
335    }
336#endif
337}
338
339/*
340 * Setup and teardown
341 */
342void
343dixResetRegistry(void)
344{
345#ifdef X_REGISTRY_REQUEST
346    ExtensionEntry extEntry = { .name = CORE };
347#endif
348
349    dixFreeRegistry();
350
351#ifdef X_REGISTRY_REQUEST
352    /* Open the protocol file */
353    fh = fopen(FILENAME, "r");
354    if (!fh)
355        LogMessage(X_WARNING,
356                   "Failed to open protocol names file " FILENAME "\n");
357
358    /* Add the core protocol */
359    RegisterExtensionNames(&extEntry);
360#endif
361
362#ifdef X_REGISTRY_RESOURCE
363    /* Add built-in resources */
364    RegisterResourceName(RT_NONE, "NONE");
365    RegisterResourceName(RT_WINDOW, "WINDOW");
366    RegisterResourceName(RT_PIXMAP, "PIXMAP");
367    RegisterResourceName(RT_GC, "GC");
368    RegisterResourceName(RT_FONT, "FONT");
369    RegisterResourceName(RT_CURSOR, "CURSOR");
370    RegisterResourceName(RT_COLORMAP, "COLORMAP");
371    RegisterResourceName(RT_CMAPENTRY, "COLORMAP ENTRY");
372    RegisterResourceName(RT_OTHERCLIENT, "OTHER CLIENT");
373    RegisterResourceName(RT_PASSIVEGRAB, "PASSIVE GRAB");
374#endif
375}
376