1/***********************************************************
2Copyright (c) 1993, Oracle and/or its affiliates.
3
4Permission is hereby granted, free of charge, to any person obtaining a
5copy of this software and associated documentation files (the "Software"),
6to deal in the Software without restriction, including without limitation
7the rights to use, copy, modify, merge, publish, distribute, sublicense,
8and/or sell copies of the Software, and to permit persons to whom the
9Software is furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice (including the next
12paragraph) shall be included in all copies or substantial portions of the
13Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21DEALINGS IN THE SOFTWARE.
22
23Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
24
25                        All Rights Reserved
26
27Permission to use, copy, modify, and distribute this software and its
28documentation for any purpose and without fee is hereby granted,
29provided that the above copyright notice appear in all copies and that
30both that copyright notice and this permission notice appear in
31supporting documentation, and that the name of Digital not be
32used in advertising or publicity pertaining to distribution of the
33software without specific, written prior permission.
34
35DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
36ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
37DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
38ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
39WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
40ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
41SOFTWARE.
42
43******************************************************************/
44
45/*
46
47Copyright 1987, 1988, 1998  The Open Group
48
49Permission to use, copy, modify, distribute, and sell this software and its
50documentation for any purpose is hereby granted without fee, provided that
51the above copyright notice appear in all copies and that both that
52copyright notice and this permission notice appear in supporting
53documentation.
54
55The above copyright notice and this permission notice shall be included in
56all copies or substantial portions of the Software.
57
58THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
59IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
60FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
61OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
62AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
63CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
64
65Except as contained in this notice, the name of The Open Group shall not be
66used in advertising or otherwise to promote the sale, use or other dealings
67in this Software without prior written authorization from The Open Group.
68
69*/
70
71#ifdef HAVE_CONFIG_H
72#include <config.h>
73#endif
74#include "IntrinsicI.h"
75#include "StringDefs.h"
76#include <ctype.h>
77#include <stdlib.h>
78#ifndef NOTASCII
79#define XK_LATIN1
80#endif
81#define XK_MISCELLANY
82#include <X11/keysymdef.h>
83
84#ifdef CACHE_TRANSLATIONS
85#ifdef REFCNT_TRANSLATIONS
86#define CACHED XtCacheAll | XtCacheRefCount
87#else
88#define CACHED XtCacheAll
89#endif
90#else
91#define CACHED XtCacheNone
92#endif
93
94#ifndef MAX
95#define MAX(a,b) (((a) > (b)) ? (a) : (b))
96#endif
97
98#ifndef MIN
99#define MIN(a,b) (((a) < (b)) ? (a) : (b))
100#endif
101
102static _Xconst char *XtNtranslationParseError = "translationParseError";
103
104typedef int EventType;
105
106#define PARSE_PROC_DECL String, Opaque, EventPtr, Boolean*
107
108typedef String(*ParseProc) (String /* str; */ ,
109                            Opaque /* closure; */ ,
110                            EventPtr /* event; */ ,
111                            Boolean * /* error */ );
112
113typedef TMShortCard Value;
114typedef void (*ModifierProc) (Value, LateBindingsPtr *, Boolean, Value *);
115
116typedef struct _ModifierRec {
117    const char *name;
118    XrmQuark signature;
119    ModifierProc modifierParseProc;
120    Value value;
121} ModifierRec, *ModifierKeys;
122
123typedef struct _EventKey {
124    const char *event;
125    XrmQuark signature;
126    EventType eventType;
127    ParseProc parseDetail;
128    Opaque closure;
129} EventKey, *EventKeys;
130
131typedef struct {
132    const char *name;
133    XrmQuark signature;
134    Value value;
135} NameValueRec, *NameValueTable;
136
137static void ParseModImmed(Value, LateBindingsPtr *, Boolean, Value *);
138static void ParseModSym(Value, LateBindingsPtr *, Boolean, Value *);
139static String PanicModeRecovery(String);
140static String CheckForPoundSign(String, _XtTranslateOp, _XtTranslateOp *);
141static KeySym StringToKeySym(String, Boolean *);
142/* *INDENT-OFF* */
143static ModifierRec modifiers[] = {
144    {"Shift",   0,      ParseModImmed, ShiftMask},
145    {"Lock",    0,      ParseModImmed, LockMask},
146    {"Ctrl",    0,      ParseModImmed, ControlMask},
147    {"Mod1",    0,      ParseModImmed, Mod1Mask},
148    {"Mod2",    0,      ParseModImmed, Mod2Mask},
149    {"Mod3",    0,      ParseModImmed, Mod3Mask},
150    {"Mod4",    0,      ParseModImmed, Mod4Mask},
151    {"Mod5",    0,      ParseModImmed, Mod5Mask},
152    {"Meta",    0,      ParseModSym,   XK_Meta_L},
153    {"m",       0,      ParseModSym,   XK_Meta_L},
154    {"h",       0,      ParseModSym,   XK_Hyper_L},
155    {"su",      0,      ParseModSym,   XK_Super_L},
156    {"a",       0,      ParseModSym,   XK_Alt_L},
157    {"Hyper",   0,      ParseModSym,   XK_Hyper_L},
158    {"Super",   0,      ParseModSym,   XK_Super_L},
159    {"Alt",     0,      ParseModSym,   XK_Alt_L},
160    {"Button1", 0,      ParseModImmed, Button1Mask},
161    {"Button2", 0,      ParseModImmed, Button2Mask},
162    {"Button3", 0,      ParseModImmed, Button3Mask},
163    {"Button4", 0,      ParseModImmed, Button4Mask},
164    {"Button5", 0,      ParseModImmed, Button5Mask},
165    {"c",       0,      ParseModImmed, ControlMask},
166    {"s",       0,      ParseModImmed, ShiftMask},
167    {"l",       0,      ParseModImmed, LockMask},
168};
169
170static NameValueRec motionDetails[] = {
171    {"Normal",              0,         NotifyNormal},
172    {"Hint",                0,         NotifyHint},
173    {NULL,                  NULLQUARK, 0},
174};
175
176static NameValueRec notifyModes[] = {
177    {"Normal",              0,         NotifyNormal},
178    {"Grab",                0,         NotifyGrab},
179    {"Ungrab",              0,         NotifyUngrab},
180    {"WhileGrabbed",        0,         NotifyWhileGrabbed},
181    {NULL,                  NULLQUARK, 0},
182};
183
184#if 0
185static NameValueRec notifyDetail[] = {
186    {"Ancestor",            0,         NotifyAncestor},
187    {"Virtual",             0,         NotifyVirtual},
188    {"Inferior",            0,         NotifyInferior},
189    {"Nonlinear",           0,         NotifyNonlinear},
190    {"NonlinearVirtual",    0,         NotifyNonlinearVirtual},
191    {"Pointer",             0,         NotifyPointer},
192    {"PointerRoot",         0,         NotifyPointerRoot},
193    {"DetailNone",          0,         NotifyDetailNone},
194    {NULL,                  NULLQUARK, 0},
195};
196
197static NameValueRec visibilityNotify[] = {
198    {"Unobscured",          0,         VisibilityUnobscured},
199    {"PartiallyObscured",   0,         VisibilityPartiallyObscured},
200    {"FullyObscured",       0,         VisibilityFullyObscured},
201    {NULL,                  NULLQUARK, 0},
202};
203
204static NameValueRec circulation[] = {
205    {"OnTop",               0,         PlaceOnTop},
206    {"OnBottom",            0,         PlaceOnBottom},
207    {NULL,                  NULLQUARK, 0},
208};
209
210static NameValueRec propertyChanged[] = {
211    {"NewValue",            0,         PropertyNewValue},
212    {"Delete",              0,         PropertyDelete},
213    {NULL,                  NULLQUARK, 0},
214};
215#endif /*0*/
216
217static NameValueRec mappingNotify[] = {
218    {"Modifier",            0,         MappingModifier},
219    {"Keyboard",            0,         MappingKeyboard},
220    {"Pointer",             0,         MappingPointer},
221    {NULL,                  NULLQUARK, 0},
222};
223/* *INDENT-ON* */
224
225static String ParseKeySym(PARSE_PROC_DECL);
226static String ParseKeyAndModifiers(PARSE_PROC_DECL);
227static String ParseTable(PARSE_PROC_DECL);
228static String ParseButton(PARSE_PROC_DECL);
229static String ParseImmed(PARSE_PROC_DECL);
230static String ParseAddModifier(PARSE_PROC_DECL);
231static String ParseNone(PARSE_PROC_DECL);
232static String ParseAtom(PARSE_PROC_DECL);
233
234/* *INDENT-OFF* */
235static EventKey events[] = {
236
237/* Event Name,    Quark, Event Type,    Detail Parser, Closure */
238
239{"KeyPress",        NULLQUARK, KeyPress,        ParseKeySym,    NULL},
240{"Key",             NULLQUARK, KeyPress,        ParseKeySym,    NULL},
241{"KeyDown",         NULLQUARK, KeyPress,        ParseKeySym,    NULL},
242{"Ctrl",            NULLQUARK, KeyPress,        ParseKeyAndModifiers, (Opaque)ControlMask},
243{"Shift",           NULLQUARK, KeyPress,        ParseKeyAndModifiers, (Opaque)ShiftMask},
244{"Meta",            NULLQUARK, KeyPress,        ParseKeyAndModifiers, (Opaque)NULL},
245{"KeyUp",           NULLQUARK, KeyRelease,      ParseKeySym,    NULL},
246{"KeyRelease",      NULLQUARK, KeyRelease,      ParseKeySym,    NULL},
247
248{"ButtonPress",     NULLQUARK, ButtonPress,     ParseButton, NULL },
249{"BtnDown",         NULLQUARK, ButtonPress,     ParseButton, NULL },
250{"Btn1Down",        NULLQUARK, ButtonPress,     ParseImmed, (Opaque)Button1},
251{"Btn2Down",        NULLQUARK, ButtonPress,     ParseImmed, (Opaque)Button2},
252{"Btn3Down",        NULLQUARK, ButtonPress,     ParseImmed, (Opaque)Button3},
253{"Btn4Down",        NULLQUARK, ButtonPress,     ParseImmed, (Opaque)Button4},
254{"Btn5Down",        NULLQUARK, ButtonPress,     ParseImmed, (Opaque)Button5},
255
256/* Event Name,    Quark, Event Type,    Detail Parser, Closure */
257
258{"ButtonRelease",   NULLQUARK, ButtonRelease,   ParseButton, NULL },
259{"BtnUp",           NULLQUARK, ButtonRelease,   ParseButton, NULL },
260{"Btn1Up",          NULLQUARK, ButtonRelease,   ParseImmed, (Opaque)Button1},
261{"Btn2Up",          NULLQUARK, ButtonRelease,   ParseImmed, (Opaque)Button2},
262{"Btn3Up",          NULLQUARK, ButtonRelease,   ParseImmed, (Opaque)Button3},
263{"Btn4Up",          NULLQUARK, ButtonRelease,   ParseImmed, (Opaque)Button4},
264{"Btn5Up",          NULLQUARK, ButtonRelease,   ParseImmed, (Opaque)Button5},
265
266{"MotionNotify",    NULLQUARK, MotionNotify,    ParseTable, (Opaque)motionDetails},
267{"PtrMoved",        NULLQUARK, MotionNotify,    ParseTable, (Opaque)motionDetails},
268{"Motion",          NULLQUARK, MotionNotify,    ParseTable, (Opaque)motionDetails},
269{"MouseMoved",      NULLQUARK, MotionNotify,    ParseTable, (Opaque)motionDetails},
270{"BtnMotion",       NULLQUARK, MotionNotify,    ParseAddModifier, (Opaque)AnyButtonMask},
271{"Btn1Motion",      NULLQUARK, MotionNotify,    ParseAddModifier, (Opaque)Button1Mask},
272{"Btn2Motion",      NULLQUARK, MotionNotify,    ParseAddModifier, (Opaque)Button2Mask},
273{"Btn3Motion",      NULLQUARK, MotionNotify,    ParseAddModifier, (Opaque)Button3Mask},
274{"Btn4Motion",      NULLQUARK, MotionNotify,    ParseAddModifier, (Opaque)Button4Mask},
275{"Btn5Motion",      NULLQUARK, MotionNotify,    ParseAddModifier, (Opaque)Button5Mask},
276
277{"EnterNotify",     NULLQUARK, EnterNotify,     ParseTable, (Opaque)notifyModes},
278{"Enter",           NULLQUARK, EnterNotify,     ParseTable, (Opaque)notifyModes},
279{"EnterWindow",     NULLQUARK, EnterNotify,     ParseTable, (Opaque)notifyModes},
280
281{"LeaveNotify",     NULLQUARK, LeaveNotify,     ParseTable, (Opaque)notifyModes},
282{"LeaveWindow",     NULLQUARK, LeaveNotify,     ParseTable, (Opaque)notifyModes},
283{"Leave",           NULLQUARK, LeaveNotify,     ParseTable, (Opaque)notifyModes},
284
285/* Event Name,    Quark, Event Type,    Detail Parser, Closure */
286
287{"FocusIn",         NULLQUARK, FocusIn,         ParseTable, (Opaque)notifyModes},
288
289{"FocusOut",        NULLQUARK, FocusOut,        ParseTable, (Opaque)notifyModes},
290
291{"KeymapNotify",    NULLQUARK, KeymapNotify,    ParseNone,      NULL},
292{"Keymap",          NULLQUARK, KeymapNotify,    ParseNone,      NULL},
293
294{"Expose",          NULLQUARK, Expose,          ParseNone,      NULL},
295
296{"GraphicsExpose",  NULLQUARK, GraphicsExpose,  ParseNone,      NULL},
297{"GrExp",           NULLQUARK, GraphicsExpose,  ParseNone,      NULL},
298
299{"NoExpose",        NULLQUARK, NoExpose,        ParseNone,      NULL},
300{"NoExp",           NULLQUARK, NoExpose,        ParseNone,      NULL},
301
302{"VisibilityNotify",NULLQUARK, VisibilityNotify,ParseNone,      NULL},
303{"Visible",         NULLQUARK, VisibilityNotify,ParseNone,      NULL},
304
305{"CreateNotify",    NULLQUARK, CreateNotify,    ParseNone,      NULL},
306{"Create",          NULLQUARK, CreateNotify,    ParseNone,      NULL},
307
308/* Event Name,    Quark, Event Type,    Detail Parser, Closure */
309
310{"DestroyNotify",   NULLQUARK, DestroyNotify,   ParseNone,      NULL},
311{"Destroy",         NULLQUARK, DestroyNotify,   ParseNone,      NULL},
312
313{"UnmapNotify",     NULLQUARK, UnmapNotify,     ParseNone,      NULL},
314{"Unmap",           NULLQUARK, UnmapNotify,     ParseNone,      NULL},
315
316{"MapNotify",       NULLQUARK, MapNotify,       ParseNone,      NULL},
317{"Map",             NULLQUARK, MapNotify,       ParseNone,      NULL},
318
319{"MapRequest",      NULLQUARK, MapRequest,      ParseNone,      NULL},
320{"MapReq",          NULLQUARK, MapRequest,      ParseNone,      NULL},
321
322{"ReparentNotify",  NULLQUARK, ReparentNotify,  ParseNone,      NULL},
323{"Reparent",        NULLQUARK, ReparentNotify,  ParseNone,      NULL},
324
325{"ConfigureNotify", NULLQUARK, ConfigureNotify, ParseNone,      NULL},
326{"Configure",       NULLQUARK, ConfigureNotify, ParseNone,      NULL},
327
328{"ConfigureRequest",NULLQUARK, ConfigureRequest,ParseNone,      NULL},
329{"ConfigureReq",    NULLQUARK, ConfigureRequest,ParseNone,      NULL},
330
331/* Event Name,    Quark, Event Type,    Detail Parser, Closure */
332
333{"GravityNotify",   NULLQUARK, GravityNotify,   ParseNone,      NULL},
334{"Grav",            NULLQUARK, GravityNotify,   ParseNone,      NULL},
335
336{"ResizeRequest",   NULLQUARK, ResizeRequest,   ParseNone,      NULL},
337{"ResReq",          NULLQUARK, ResizeRequest,   ParseNone,      NULL},
338
339{"CirculateNotify", NULLQUARK, CirculateNotify, ParseNone,      NULL},
340{"Circ",            NULLQUARK, CirculateNotify, ParseNone,      NULL},
341
342{"CirculateRequest",NULLQUARK, CirculateRequest,ParseNone,      NULL},
343{"CircReq",         NULLQUARK, CirculateRequest,ParseNone,      NULL},
344
345{"PropertyNotify",  NULLQUARK, PropertyNotify,  ParseAtom,      NULL},
346{"Prop",            NULLQUARK, PropertyNotify,  ParseAtom,      NULL},
347
348{"SelectionClear",  NULLQUARK, SelectionClear,  ParseAtom,      NULL},
349{"SelClr",          NULLQUARK, SelectionClear,  ParseAtom,      NULL},
350
351{"SelectionRequest",NULLQUARK, SelectionRequest,ParseAtom,      NULL},
352{"SelReq",          NULLQUARK, SelectionRequest,ParseAtom,      NULL},
353
354/* Event Name,    Quark, Event Type,    Detail Parser, Closure */
355
356{"SelectionNotify", NULLQUARK, SelectionNotify, ParseAtom,      NULL},
357{"Select",          NULLQUARK, SelectionNotify, ParseAtom,      NULL},
358
359{"ColormapNotify",  NULLQUARK, ColormapNotify,  ParseNone,      NULL},
360{"Clrmap",          NULLQUARK, ColormapNotify,  ParseNone,      NULL},
361
362{"ClientMessage",   NULLQUARK, ClientMessage,   ParseAtom,      NULL},
363{"Message",         NULLQUARK, ClientMessage,   ParseAtom,      NULL},
364
365{"MappingNotify",   NULLQUARK, MappingNotify,   ParseTable, (Opaque)mappingNotify},
366{"Mapping",         NULLQUARK, MappingNotify,   ParseTable, (Opaque)mappingNotify},
367
368#ifdef DEBUG
369# ifdef notdef
370{"Timer",           NULLQUARK, _XtTimerEventType, ParseNone,     NULL},
371{"EventTimer",      NULLQUARK, _XtEventTimerEventType, ParseNone,NULL},
372# endif /* notdef */
373#endif /* DEBUG */
374
375/* Event Name,    Quark, Event Type,    Detail Parser, Closure */
376
377};
378/* *INDENT-ON* */
379
380#define IsNewline(str) ((str) == '\n')
381
382#define ScanFor(str, ch) \
383    while ((*(str) != (ch)) && (*(str) != '\0') && !IsNewline(*(str))) (str)++
384
385#define ScanNumeric(str)  while ('0' <= *(str) && *(str) <= '9') (str)++
386
387#define ScanAlphanumeric(str) \
388    while (('A' <= *(str) && *(str) <= 'Z') || \
389           ('a' <= *(str) && *(str) <= 'z') || \
390           ('0' <= *(str) && *(str) <= '9')) (str)++
391
392#define ScanWhitespace(str) \
393    while (*(str) == ' ' || *(str) == '\t') (str)++
394
395static Boolean initialized = FALSE;
396static XrmQuark QMeta;
397static XrmQuark QCtrl;
398static XrmQuark QNone;
399static XrmQuark QAny;
400
401static void
402FreeEventSeq(EventSeqPtr eventSeq)
403{
404    register EventSeqPtr evs = eventSeq;
405
406    while (evs != NULL) {
407        evs->state = (StatePtr) evs;
408        if (evs->next != NULL && evs->next->state == (StatePtr) evs->next)
409            evs->next = NULL;
410        evs = evs->next;
411    }
412
413    evs = eventSeq;
414    while (evs != NULL) {
415        register EventPtr event = evs;
416
417        evs = evs->next;
418        if (evs == event)
419            evs = NULL;
420        XtFree((char *) event);
421    }
422}
423
424static void
425CompileNameValueTable(NameValueTable table)
426{
427    register int i;
428
429    for (i = 0; table[i].name; i++)
430        table[i].signature = XrmPermStringToQuark(table[i].name);
431}
432
433static int
434OrderEvents(_Xconst void *a, _Xconst void *b)
435{
436    return ((((_Xconst EventKey *) a)->signature <
437             ((_Xconst EventKey *) b)->signature) ? -1 : 1);
438}
439
440static void
441Compile_XtEventTable(EventKeys table, Cardinal count)
442{
443    register int i;
444    register EventKeys entry = table;
445
446    for (i = (int) count; --i >= 0; entry++)
447        entry->signature = XrmPermStringToQuark(entry->event);
448    qsort(table, count, sizeof(EventKey), OrderEvents);
449}
450
451static int
452OrderModifiers(_Xconst void *a, _Xconst void *b)
453{
454    return ((((_Xconst ModifierRec *) a)->signature <
455             ((_Xconst ModifierRec *) b)->signature) ? -1 : 1);
456}
457
458static void
459Compile_XtModifierTable(ModifierKeys table, Cardinal count)
460{
461    register int i;
462    register ModifierKeys entry = table;
463
464    for (i = (int) count; --i >= 0; entry++)
465        entry->signature = XrmPermStringToQuark(entry->name);
466    qsort(table, count, sizeof(ModifierRec), OrderModifiers);
467}
468
469static String
470PanicModeRecovery(String str)
471{
472    ScanFor(str, '\n');
473    if (*str == '\n')
474        str++;
475    return str;
476
477}
478
479static void
480Syntax(_Xconst char *str0, _Xconst char *str1)
481{
482    Cardinal num_params = 2;
483    String params[2];
484
485    params[0] = (String) str0;
486    params[1] = (String) str1;
487    XtWarningMsg(XtNtranslationParseError, "parseError", XtCXtToolkitError,
488                 "translation table syntax error: %s %s", params, &num_params);
489}
490
491static Cardinal
492LookupTMEventType(String eventStr, Boolean *error)
493{
494    register int i = 0, left, right;
495    register XrmQuark signature;
496    static int previous = 0;
497
498    LOCK_PROCESS;
499    if ((signature = StringToQuark(eventStr)) == events[previous].signature) {
500        UNLOCK_PROCESS;
501        return (Cardinal) previous;
502    }
503
504    left = 0;
505    right = XtNumber(events) - 1;
506    while (left <= right) {
507        i = (left + right) >> 1;
508        if (signature < events[i].signature)
509            right = i - 1;
510        else if (signature > events[i].signature)
511            left = i + 1;
512        else {
513            previous = i;
514            UNLOCK_PROCESS;
515            return (Cardinal) i;
516        }
517    }
518
519    Syntax("Unknown event type :  ", eventStr);
520    *error = TRUE;
521    UNLOCK_PROCESS;
522    return (Cardinal) i;
523}
524
525static void
526StoreLateBindings(KeySym keysymL,
527                  Boolean notL,
528                  KeySym keysymR,
529                  Boolean notR,
530                  LateBindingsPtr *lateBindings)
531{
532    LateBindingsPtr temp;
533
534    if (lateBindings != NULL) {
535        Boolean pair = FALSE;
536        unsigned long count;
537        unsigned long number;
538
539        temp = *lateBindings;
540        if (temp != NULL) {
541            for (count = 0; temp[count].keysym; count++) {
542                /*EMPTY*/
543            }
544        }
545        else
546            count = 0;
547        if (!keysymR) {
548            number = 1;
549            pair = FALSE;
550        }
551        else {
552            number = 2;
553            pair = TRUE;
554        }
555
556        temp = XtReallocArray(temp, (Cardinal) (count + number + 1),
557                              (Cardinal) sizeof(LateBindings));
558        *lateBindings = temp;
559        XtSetBit(temp[count].knot, notL);
560        XtSetBit(temp[count].pair, pair);
561        if (count == 0)
562            temp[count].ref_count = 1;
563        temp[count++].keysym = keysymL;
564        if (keysymR) {
565            XtSetBit(temp[count].knot, notR);
566            temp[count].pair = FALSE;
567            temp[count].ref_count = 0;
568            temp[count++].keysym = keysymR;
569        }
570        temp[count].knot = temp[count].pair = FALSE;
571        temp[count].ref_count = 0;
572        temp[count].keysym = 0;
573    }
574}
575
576static void
577_XtParseKeysymMod(String name,
578                  LateBindingsPtr *lateBindings,
579                  Boolean notFlag,
580                  Value *valueP,
581                  Boolean *error)
582{
583    KeySym keySym;
584
585    keySym = StringToKeySym(name, error);
586    *valueP = 0;
587    if (keySym != NoSymbol) {
588        StoreLateBindings(keySym, notFlag, (KeySym) NULL, FALSE, lateBindings);
589    }
590}
591
592static Boolean
593_XtLookupModifier(XrmQuark signature,
594                  LateBindingsPtr *lateBindings,
595                  Boolean notFlag,
596                  Value *valueP,
597                  Bool constMask)
598{
599    int left, right;
600    static int previous = 0;
601
602    LOCK_PROCESS;
603    if (signature == modifiers[previous].signature) {
604        if (constMask)
605            *valueP = modifiers[previous].value;
606        else                    /* if (modifiers[previous].modifierParseProc) always true */
607            (*modifiers[previous].modifierParseProc)
608                (modifiers[previous].value, lateBindings, notFlag, valueP);
609        UNLOCK_PROCESS;
610        return TRUE;
611    }
612
613    left = 0;
614    right = XtNumber(modifiers) - 1;
615    while (left <= right) {
616        int i = (left + right) >> 1;
617
618        if (signature < modifiers[i].signature)
619            right = i - 1;
620        else if (signature > modifiers[i].signature)
621            left = i + 1;
622        else {
623            previous = i;
624            if (constMask)
625                *valueP = modifiers[i].value;
626            else                /* if (modifiers[i].modifierParseProc) always true */
627                (*modifiers[i].modifierParseProc)
628                    (modifiers[i].value, lateBindings, notFlag, valueP);
629            UNLOCK_PROCESS;
630            return TRUE;
631        }
632    }
633    UNLOCK_PROCESS;
634    return FALSE;
635}
636
637static String
638ScanIdent(register String str)
639{
640    ScanAlphanumeric(str);
641    while (('A' <= *str && *str <= 'Z')
642           || ('a' <= *str && *str <= 'z')
643           || ('0' <= *str && *str <= '9')
644           || (*str == '-')
645           || (*str == '_')
646           || (*str == '$')
647        )
648        str++;
649    return str;
650}
651
652static String
653FetchModifierToken(String str, XrmQuark *token_return)
654{
655    String start = str;
656
657    if (*str == '$') {
658        *token_return = QMeta;
659        str++;
660    }
661    else if (*str == '^') {
662        *token_return = QCtrl;
663        str++;
664    }
665    else {
666        str = ScanIdent(str);
667        if (start != str) {
668            char modStrbuf[100];
669            char *modStr;
670
671            modStr = XtStackAlloc((size_t) (str - start + 1), modStrbuf);
672            if (modStr == NULL)
673                _XtAllocError(NULL);
674            (void) memcpy(modStr, start, (size_t) (str - start));
675            modStr[str - start] = '\0';
676            *token_return = XrmStringToQuark(modStr);
677            XtStackFree(modStr, modStrbuf);
678        }
679    }
680    return str;
681}
682
683static String
684ParseModifiers(register String str, EventPtr event, Boolean *error)
685{
686    register String start;
687    Boolean notFlag, exclusive, keysymAsMod;
688    Value maskBit;
689    XrmQuark Qmod = QNone;
690
691    ScanWhitespace(str);
692    start = str;
693    str = FetchModifierToken(str, &Qmod);
694    exclusive = FALSE;
695    if (start != str) {
696        if (Qmod == QNone) {
697            event->event.modifierMask = (unsigned long) (~0);
698            event->event.modifiers = 0;
699            ScanWhitespace(str);
700            return str;
701        }
702        else if (Qmod == QAny) {        /*backward compatibility */
703            event->event.modifierMask = 0;
704            event->event.modifiers = AnyModifier;
705            ScanWhitespace(str);
706            return str;
707        }
708        str = start;            /*if plain modifier, reset to beginning */
709    }
710    else
711        while (*str == '!' || *str == ':') {
712            if (*str == '!') {
713                exclusive = TRUE;
714                str++;
715                ScanWhitespace(str);
716            }
717            if (*str == ':') {
718                event->event.standard = TRUE;
719                str++;
720                ScanWhitespace(str);
721            }
722        }
723
724    while (*str != '<') {
725        if (*str == '~') {
726            notFlag = TRUE;
727            str++;
728        }
729        else
730            notFlag = FALSE;
731        if (*str == '@') {
732            keysymAsMod = TRUE;
733            str++;
734        }
735        else
736            keysymAsMod = FALSE;
737        start = str;
738        str = FetchModifierToken(str, &Qmod);
739        if (start == str) {
740            Syntax("Modifier or '<' expected", "");
741            *error = TRUE;
742            return PanicModeRecovery(str);
743        }
744        if (keysymAsMod) {
745            _XtParseKeysymMod(XrmQuarkToString(Qmod),
746                              &event->event.lateModifiers,
747                              notFlag, &maskBit, error);
748            if (*error)
749                return PanicModeRecovery(str);
750
751        }
752        else if (!_XtLookupModifier(Qmod, &event->event.lateModifiers,
753                                    notFlag, &maskBit, FALSE)) {
754            Syntax("Unknown modifier name:  ", XrmQuarkToString(Qmod));
755            *error = TRUE;
756            return PanicModeRecovery(str);
757        }
758        event->event.modifierMask |= maskBit;
759        if (notFlag)
760            event->event.modifiers =
761                (event->event.modifiers & (TMLongCard) (~maskBit));
762        else
763            event->event.modifiers |= maskBit;
764        ScanWhitespace(str);
765    }
766    if (exclusive)
767        event->event.modifierMask = (unsigned long) (~0);
768    return str;
769}
770
771static String
772ParseXtEventType(register String str,
773                 EventPtr event,
774                 Cardinal *tmEventP,
775                 Boolean *error)
776{
777    String start = str;
778    char eventTypeStrbuf[100];
779    char *eventTypeStr;
780
781    ScanAlphanumeric(str);
782    eventTypeStr = XtStackAlloc((size_t) (str - start + 1), eventTypeStrbuf);
783    if (eventTypeStr == NULL)
784        _XtAllocError(NULL);
785    (void) memcpy(eventTypeStr, start, (size_t) (str - start));
786    eventTypeStr[str - start] = '\0';
787    *tmEventP = LookupTMEventType(eventTypeStr, error);
788    XtStackFree(eventTypeStr, eventTypeStrbuf);
789    if (*error)
790        return PanicModeRecovery(str);
791    event->event.eventType = (TMLongCard) events[*tmEventP].eventType;
792    return str;
793}
794
795static unsigned long
796StrToHex(String str)
797{
798    register char c;
799    register unsigned long val = 0;
800
801    while ((c = *str)) {
802        if ('0' <= c && c <= '9')
803            val = (unsigned long) (val * 16 + (unsigned long) c - '0');
804        else if ('a' <= c && c <= 'z')
805            val = (unsigned long) (val * 16 + (unsigned long) c - 'a' + 10);
806        else if ('A' <= c && c <= 'Z')
807            val = (unsigned long) (val * 16 + (unsigned long) c - 'A' + 10);
808        else
809            return 0;
810        str++;
811    }
812
813    return val;
814}
815
816static unsigned long
817StrToOct(String str)
818{
819    register char c;
820    register unsigned long val = 0;
821
822    while ((c = *str)) {
823        if ('0' <= c && c <= '7')
824            val = val * 8 + (unsigned long) c - '0';
825        else
826            return 0;
827        str++;
828    }
829
830    return val;
831}
832
833static unsigned long
834StrToNum(String str)
835{
836    register char c;
837    register unsigned long val = 0;
838
839    if (*str == '0') {
840        str++;
841        if (*str == 'x' || *str == 'X')
842            return StrToHex(++str);
843        else
844            return StrToOct(str);
845    }
846
847    while ((c = *str)) {
848        if ('0' <= c && c <= '9')
849            val = val * 10 + (unsigned long) c - '0';
850        else
851            return 0;
852        str++;
853    }
854
855    return val;
856}
857
858static KeySym
859StringToKeySym(String str, Boolean *error)
860{
861    KeySym k;
862
863    if (str == NULL || *str == '\0')
864        return (KeySym) 0;
865
866#ifndef NOTASCII
867    /* special case single character ASCII, for speed */
868    if (*(str + 1) == '\0') {
869        if (' ' <= *str && *str <= '~')
870            return (KeySym) (XK_space + (*str - ' '));
871    }
872#endif
873
874    if ('0' <= *str && *str <= '9')
875        return (KeySym) StrToNum(str);
876    k = XStringToKeysym(str);
877    if (k != NoSymbol)
878        return k;
879
880#ifdef NOTASCII
881    /* fall-back case to preserve backwards compatibility; no-one
882     * should be relying upon this!
883     */
884    if (*(str + 1) == '\0')
885        return (KeySym) * str;
886#endif
887
888    Syntax("Unknown keysym name: ", str);
889    *error = TRUE;
890    return NoSymbol;
891}
892
893static void
894ParseModImmed(Value value,
895              LateBindingsPtr *lateBindings _X_UNUSED,
896              Boolean notFlag _X_UNUSED,
897              Value *valueP)
898{
899    *valueP = value;
900}
901
902/* is only valid with keysyms that have an _L and _R in their name;
903 * and ignores keysym lookup errors (i.e. assumes only valid keysyms)
904 */
905static void
906ParseModSym(Value value,
907            LateBindingsPtr *lateBindings, Boolean notFlag, Value *valueP)
908{
909    register KeySym keysymL = (KeySym) value;
910    register KeySym keysymR = keysymL + 1;      /* valid for supported keysyms */
911
912    StoreLateBindings(keysymL, notFlag, keysymR, notFlag, lateBindings);
913    *valueP = 0;
914}
915
916#ifdef sparc
917/*
918 * The stupid optimizer in SunOS 4.0.3 and below generates bogus code that
919 * causes the value of the most recently used variable to be returned instead
920 * of the value passed in.
921 */
922static String stupid_optimizer_kludge;
923
924#define BROKEN_OPTIMIZER_HACK(val) stupid_optimizer_kludge = (val)
925#else
926#define BROKEN_OPTIMIZER_HACK(val) val
927#endif
928
929static String
930ParseImmed(register String str,
931           register Opaque closure,
932           register EventPtr event,
933           Boolean *error _X_UNUSED)
934{
935    event->event.eventCode = (unsigned long) closure;
936    event->event.eventCodeMask = (unsigned long) (~0UL);
937
938    return BROKEN_OPTIMIZER_HACK(str);
939}
940
941static String
942ParseAddModifier(register String str,
943                 register Opaque closure,
944                 register EventPtr event,
945                 Boolean *error _X_UNUSED)
946{
947    register unsigned long modval = (unsigned long) closure;
948
949    event->event.modifiers |= modval;
950    if (modval != AnyButtonMask)        /* AnyButtonMask is don't-care mask */
951        event->event.modifierMask |= modval;
952
953    return BROKEN_OPTIMIZER_HACK(str);
954}
955
956static String
957ParseKeyAndModifiers(String str,
958                     Opaque closure,
959                     EventPtr event,
960                     Boolean *error)
961{
962    str = ParseKeySym(str, closure, event, error);
963    if ((unsigned long) closure == 0) {
964        Value metaMask;         /* unused */
965
966        (void) _XtLookupModifier(QMeta, &event->event.lateModifiers, FALSE,
967                                 &metaMask, FALSE);
968    }
969    else {
970        event->event.modifiers |= (unsigned long) closure;
971        event->event.modifierMask |= (unsigned long) closure;
972    }
973    return str;
974}
975
976static String
977ParseKeySym(register String str,
978            Opaque closure _X_UNUSED,
979            EventPtr event,
980            Boolean *error)
981{
982    String start;
983    char keySymNamebuf[100];
984    char *keySymName = NULL;
985
986    ScanWhitespace(str);
987
988    if (*str == '\\') {
989        keySymName = keySymNamebuf;
990        str++;
991        keySymName[0] = *str;
992        if (*str != '\0' && !IsNewline(*str))
993            str++;
994        keySymName[1] = '\0';
995        event->event.eventCode = StringToKeySym(keySymName, error);
996        event->event.eventCodeMask = (unsigned long) (~0L);
997    }
998    else if (*str == ',' || *str == ':' ||
999             /* allow leftparen to be single char symbol,
1000              * for backwards compatibility
1001              */
1002             (*str == '(' && *(str + 1) >= '0' && *(str + 1) <= '9')) {
1003        keySymName = keySymNamebuf;     /* just so we can stackfree it later */
1004        keySymName[0] = '\0';
1005        /* no detail */
1006        event->event.eventCode = 0L;
1007        event->event.eventCodeMask = 0L;
1008    }
1009    else {
1010        start = str;
1011        while (*str != ','
1012               && *str != ':' && *str != ' ' && *str != '\t' && !IsNewline(*str)
1013               && (*str != '(' || *(str + 1) <= '0' || *(str + 1) >= '9')
1014               && *str != '\0')
1015            str++;
1016        keySymName = XtStackAlloc((size_t) (str - start + 1), keySymNamebuf);
1017        (void) memcpy(keySymName, start, (size_t) (str - start));
1018        keySymName[str - start] = '\0';
1019        event->event.eventCode = StringToKeySym(keySymName, error);
1020        event->event.eventCodeMask = (unsigned long) (~0L);
1021    }
1022    if (*error && keySymName) {
1023        /* We never get here when keySymName hasn't been allocated */
1024        if (keySymName[0] == '<') {
1025            /* special case for common error */
1026            XtWarningMsg(XtNtranslationParseError, "missingComma",
1027                         XtCXtToolkitError,
1028                         "... possibly due to missing ',' in event sequence.",
1029                         (String *) NULL, (Cardinal *) NULL);
1030        }
1031        XtStackFree(keySymName, keySymNamebuf);
1032        return PanicModeRecovery(str);
1033    }
1034    if (event->event.standard)
1035        event->event.matchEvent = _XtMatchUsingStandardMods;
1036    else
1037        event->event.matchEvent = _XtMatchUsingDontCareMods;
1038
1039    XtStackFree(keySymName, keySymNamebuf);
1040
1041    return str;
1042}
1043
1044static String
1045ParseTable(register String str, Opaque closure, EventPtr event, Boolean *error)
1046{
1047    register String start = str;
1048    register XrmQuark signature;
1049    NameValueTable table = (NameValueTable) closure;
1050    char tableSymName[100];
1051
1052    event->event.eventCode = 0L;
1053    ScanAlphanumeric(str);
1054    if (str == start) {
1055        event->event.eventCodeMask = 0L;
1056        return str;
1057    }
1058    if (str - start >= 99) {
1059        Syntax("Invalid Detail Type (string is too long).", "");
1060        *error = TRUE;
1061        return str;
1062    }
1063    (void) memcpy(tableSymName, start, (size_t) (str - start));
1064    tableSymName[str - start] = '\0';
1065    signature = StringToQuark(tableSymName);
1066    for (; table->signature != NULLQUARK; table++)
1067        if (table->signature == signature) {
1068            event->event.eventCode = table->value;
1069            event->event.eventCodeMask = (unsigned long) (~0L);
1070            return str;
1071        }
1072
1073    Syntax("Unknown Detail Type:  ", tableSymName);
1074    *error = TRUE;
1075    return PanicModeRecovery(str);
1076}
1077
1078static String
1079ParseButton(String str, Opaque closure _X_UNUSED, EventPtr event, Boolean *error)
1080{
1081    String start = str;
1082    char buttonStr[7];
1083    size_t len;
1084    static const char buttonPrefix[] = "Button";
1085    unsigned long button;
1086
1087    event->event.eventCode = 0L;
1088    if (strncmp(str, buttonPrefix, sizeof(buttonPrefix)-1) != 0) {
1089	event->event.eventCodeMask = 0L;
1090	return str;
1091    }
1092    str += sizeof(buttonPrefix)-1;
1093    start = str;
1094    ScanNumeric(str);
1095    if (str == start) {
1096	Syntax("Missing button number", "");
1097	*error = TRUE;
1098	return PanicModeRecovery(str);
1099    }
1100    len = (size_t) (str - start);
1101    if (len >= sizeof buttonStr) {
1102	Syntax("Button number too long", "");
1103	*error = TRUE;
1104	return PanicModeRecovery(str);
1105    }
1106    (void) memcpy(buttonStr, start, len);
1107    buttonStr[len] = '\0';
1108    button = StrToNum(buttonStr);
1109    if (button < 1 || 255 < button) {
1110	Syntax("Invalid button number", buttonStr);
1111	*error = TRUE;
1112	return PanicModeRecovery(str);
1113    }
1114    event->event.eventCode = button;
1115    event->event.eventCodeMask = (unsigned long) (~0L);
1116    return str;
1117}
1118
1119static String
1120ParseNone(String str,
1121          Opaque closure _X_UNUSED,
1122          EventPtr event,
1123          Boolean *error _X_UNUSED)
1124{
1125    event->event.eventCode = 0;
1126    event->event.eventCodeMask = 0;
1127
1128    return BROKEN_OPTIMIZER_HACK(str);
1129}
1130
1131static String
1132ParseAtom(String str, Opaque closure _X_UNUSED, EventPtr event, Boolean *error)
1133{
1134    ScanWhitespace(str);
1135
1136    if (*str == ',' || *str == ':') {
1137        /* no detail */
1138        event->event.eventCode = 0L;
1139        event->event.eventCodeMask = 0L;
1140    }
1141    else {
1142        String start;
1143        char atomName[1000];
1144
1145        start = str;
1146        while (*str != ','
1147               && *str != ':' && *str != ' ' && *str != '\t' && !IsNewline(*str)
1148               && *str != '\0')
1149            str++;
1150        if (str - start >= 999) {
1151            Syntax("Atom name must be less than 1000 characters long.", "");
1152            *error = TRUE;
1153            return str;
1154        }
1155        (void) memcpy(atomName, start, (size_t) (str - start));
1156        atomName[str - start] = '\0';
1157        event->event.eventCode = (TMLongCard) XrmStringToQuark(atomName);
1158        event->event.matchEvent = _XtMatchAtom;
1159    }
1160    return str;
1161}
1162
1163static ModifierMask buttonModifierMasks[] = {
1164    0, Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask
1165};
1166
1167static String ParseRepeat(String, int *, Boolean *, Boolean *);
1168
1169static String
1170ParseEvent(register String str,
1171           EventPtr event, int *reps,
1172           Boolean *plus,
1173           Boolean *error)
1174{
1175    Cardinal tmEvent;
1176
1177    str = ParseModifiers(str, event, error);
1178    if (*error)
1179        return str;
1180    if (*str != '<') {
1181        Syntax("Missing '<' while parsing event type.", "");
1182        *error = TRUE;
1183        return PanicModeRecovery(str);
1184    }
1185    else
1186        str++;
1187    str = ParseXtEventType(str, event, &tmEvent, error);
1188    if (*error)
1189        return str;
1190    if (*str != '>') {
1191        Syntax("Missing '>' while parsing event type", "");
1192        *error = TRUE;
1193        return PanicModeRecovery(str);
1194    }
1195    else
1196        str++;
1197    if (*str == '(') {
1198        str = ParseRepeat(str, reps, plus, error);
1199        if (*error)
1200            return str;
1201    }
1202    str =
1203        (*(events[tmEvent].parseDetail)) (str, events[tmEvent].closure, event,
1204                                          error);
1205    if (*error)
1206        return str;
1207
1208/* gross hack! ||| this kludge is related to the X11 protocol deficiency w.r.t.
1209 * modifiers in grabs.
1210 */
1211    if ((event->event.eventType == ButtonRelease)
1212        && (event->event.modifiers | event->event.modifierMask) /* any */
1213        &&(event->event.modifiers != AnyModifier)) {
1214        event->event.modifiers = (event->event.modifiers
1215                                  | (TMLongCard) buttonModifierMasks[event->
1216                                                                     event.
1217                                                                     eventCode]);
1218        /* the button that is going up will always be in the modifiers... */
1219    }
1220
1221    return str;
1222}
1223
1224static String
1225ParseQuotedStringEvent(register String str,
1226                       register EventPtr event,
1227                       Boolean *error)
1228{
1229    Value metaMask;
1230    char s[2];
1231
1232    if (*str == '^') {
1233        str++;
1234        event->event.modifiers = ControlMask;
1235    }
1236    else if (*str == '$') {
1237        str++;
1238        (void) _XtLookupModifier(QMeta, &event->event.lateModifiers, FALSE,
1239                                 &metaMask, FALSE);
1240    }
1241    if (*str == '\\')
1242        str++;
1243    s[0] = *str;
1244    s[1] = '\0';
1245    if (*str != '\0' && !IsNewline(*str))
1246        str++;
1247    event->event.eventType = KeyPress;
1248    event->event.eventCode = StringToKeySym(s, error);
1249    if (*error)
1250        return PanicModeRecovery(str);
1251    event->event.eventCodeMask = (unsigned long) (~0L);
1252    event->event.matchEvent = _XtMatchUsingStandardMods;
1253    event->event.standard = TRUE;
1254
1255    return str;
1256}
1257
1258static EventSeqRec timerEventRec = {
1259    {0, 0, NULL, _XtEventTimerEventType, 0L, 0L, NULL, False},
1260    /* (StatePtr) -1 */ NULL,
1261    NULL,
1262    NULL
1263};
1264
1265static void
1266RepeatDown(EventPtr *eventP, int reps, ActionPtr **actionsP)
1267{
1268    EventRec upEventRec;
1269    register EventPtr event, downEvent;
1270    EventPtr upEvent = &upEventRec;
1271    register int i;
1272
1273    downEvent = event = *eventP;
1274    *upEvent = *downEvent;
1275    upEvent->event.eventType = ((event->event.eventType == ButtonPress) ?
1276                                ButtonRelease : KeyRelease);
1277    if ((upEvent->event.eventType == ButtonRelease)
1278        && (upEvent->event.modifiers != AnyModifier)
1279        && (upEvent->event.modifiers | upEvent->event.modifierMask))
1280        upEvent->event.modifiers = (upEvent->event.modifiers
1281                                    | (TMLongCard) buttonModifierMasks[event->
1282                                                                       event.
1283                                                                       eventCode]);
1284
1285    if (event->event.lateModifiers)
1286        event->event.lateModifiers->ref_count =
1287            (unsigned short) (event->event.lateModifiers->ref_count +
1288                              (reps - 1) * 2);
1289
1290    for (i = 1; i < reps; i++) {
1291
1292        /* up */
1293        event->next = XtNew(EventSeqRec);
1294        event = event->next;
1295        *event = *upEvent;
1296
1297        /* timer */
1298        event->next = XtNew(EventSeqRec);
1299        event = event->next;
1300        *event = timerEventRec;
1301
1302        /* down */
1303        event->next = XtNew(EventSeqRec);
1304        event = event->next;
1305        *event = *downEvent;
1306
1307    }
1308
1309    event->next = NULL;
1310    *eventP = event;
1311    *actionsP = &event->actions;
1312}
1313
1314static void
1315RepeatDownPlus(EventPtr *eventP, int reps, ActionPtr **actionsP)
1316{
1317    EventRec upEventRec;
1318    register EventPtr event, downEvent, lastDownEvent = NULL;
1319    EventPtr upEvent = &upEventRec;
1320    register int i;
1321
1322    downEvent = event = *eventP;
1323    *upEvent = *downEvent;
1324    upEvent->event.eventType = ((event->event.eventType == ButtonPress) ?
1325                                ButtonRelease : KeyRelease);
1326    if ((upEvent->event.eventType == ButtonRelease)
1327        && (upEvent->event.modifiers != AnyModifier)
1328        && (upEvent->event.modifiers | upEvent->event.modifierMask))
1329        upEvent->event.modifiers = (upEvent->event.modifiers
1330                                    | (TMLongCard) buttonModifierMasks[event->
1331                                                                       event.
1332                                                                       eventCode]);
1333
1334    if (event->event.lateModifiers)
1335        event->event.lateModifiers->ref_count =
1336            (unsigned short) (event->event.lateModifiers->ref_count + reps * 2 -
1337                              1);
1338
1339    for (i = 0; i < reps; i++) {
1340
1341        if (i > 0) {
1342            /* down */
1343            event->next = XtNew(EventSeqRec);
1344            event = event->next;
1345            *event = *downEvent;
1346        }
1347        lastDownEvent = event;
1348
1349        /* up */
1350        event->next = XtNew(EventSeqRec);
1351        event = event->next;
1352        *event = *upEvent;
1353
1354        /* timer */
1355        event->next = XtNew(EventSeqRec);
1356        event = event->next;
1357        *event = timerEventRec;
1358
1359    }
1360
1361    event->next = lastDownEvent;
1362    *eventP = event;
1363    *actionsP = &lastDownEvent->actions;
1364}
1365
1366static void
1367RepeatUp(EventPtr *eventP, int reps, ActionPtr **actionsP)
1368{
1369    EventRec upEventRec;
1370    register EventPtr event, downEvent;
1371    EventPtr upEvent = &upEventRec;
1372    register int i;
1373
1374    /* the event currently sitting in *eventP is an "up" event */
1375    /* we want to make it a "down" event followed by an "up" event, */
1376    /* so that sequence matching on the "state" side works correctly. */
1377
1378    downEvent = event = *eventP;
1379    *upEvent = *downEvent;
1380    downEvent->event.eventType = ((event->event.eventType == ButtonRelease) ?
1381                                  ButtonPress : KeyPress);
1382    if ((downEvent->event.eventType == ButtonPress)
1383        && (downEvent->event.modifiers != AnyModifier)
1384        && (downEvent->event.modifiers | downEvent->event.modifierMask))
1385        downEvent->event.modifiers = (downEvent->event.modifiers
1386                                      &
1387                                      (TMLongCard) (~buttonModifierMasks
1388                                                    [event->event.eventCode]));
1389
1390    if (event->event.lateModifiers)
1391        event->event.lateModifiers->ref_count =
1392            (unsigned short) (event->event.lateModifiers->ref_count + reps * 2 -
1393                              1);
1394
1395    /* up */
1396    event->next = XtNew(EventSeqRec);
1397    event = event->next;
1398    *event = *upEvent;
1399
1400    for (i = 1; i < reps; i++) {
1401
1402        /* timer */
1403        event->next = XtNew(EventSeqRec);
1404        event = event->next;
1405        *event = timerEventRec;
1406
1407        /* down */
1408        event->next = XtNew(EventSeqRec);
1409        event = event->next;
1410        *event = *downEvent;
1411
1412        /* up */
1413        event->next = XtNew(EventSeqRec);
1414        event = event->next;
1415        *event = *upEvent;
1416
1417    }
1418
1419    event->next = NULL;
1420    *eventP = event;
1421    *actionsP = &event->actions;
1422}
1423
1424static void
1425RepeatUpPlus(EventPtr *eventP, int reps, ActionPtr **actionsP)
1426{
1427    EventRec upEventRec;
1428    register EventPtr event, downEvent, lastUpEvent = NULL;
1429    EventPtr upEvent = &upEventRec;
1430    register int i;
1431
1432    /* the event currently sitting in *eventP is an "up" event */
1433    /* we want to make it a "down" event followed by an "up" event, */
1434    /* so that sequence matching on the "state" side works correctly. */
1435
1436    downEvent = event = *eventP;
1437    *upEvent = *downEvent;
1438    downEvent->event.eventType = ((event->event.eventType == ButtonRelease) ?
1439                                  ButtonPress : KeyPress);
1440    if ((downEvent->event.eventType == ButtonPress)
1441        && (downEvent->event.modifiers != AnyModifier)
1442        && (downEvent->event.modifiers | downEvent->event.modifierMask))
1443        downEvent->event.modifiers = (downEvent->event.modifiers
1444                                      &
1445                                      (TMLongCard) (~buttonModifierMasks
1446                                                    [event->event.eventCode]));
1447
1448    if (event->event.lateModifiers)
1449        event->event.lateModifiers->ref_count =
1450            (unsigned short) (event->event.lateModifiers->ref_count + reps * 2);
1451
1452    for (i = 0; i < reps; i++) {
1453
1454        /* up */
1455        event->next = XtNew(EventSeqRec);
1456        lastUpEvent = event = event->next;
1457        *event = *upEvent;
1458
1459        /* timer */
1460        event->next = XtNew(EventSeqRec);
1461        event = event->next;
1462        *event = timerEventRec;
1463
1464        /* down */
1465        event->next = XtNew(EventSeqRec);
1466        event = event->next;
1467        *event = *downEvent;
1468
1469    }
1470
1471    event->next = lastUpEvent;
1472    *eventP = event;
1473    *actionsP = &lastUpEvent->actions;
1474}
1475
1476static void
1477RepeatOther(EventPtr *eventP, int reps, ActionPtr **actionsP)
1478{
1479    register EventPtr event, tempEvent;
1480    register int i;
1481
1482    tempEvent = event = *eventP;
1483
1484    if (event->event.lateModifiers)
1485        event->event.lateModifiers->ref_count =
1486            (unsigned short) (event->event.lateModifiers->ref_count + reps - 1);
1487
1488    for (i = 1; i < reps; i++) {
1489        event->next = XtNew(EventSeqRec);
1490        event = event->next;
1491        *event = *tempEvent;
1492    }
1493
1494    *eventP = event;
1495    *actionsP = &event->actions;
1496}
1497
1498static void
1499RepeatOtherPlus(EventPtr *eventP, int reps, ActionPtr **actionsP)
1500{
1501    register EventPtr event, tempEvent;
1502    register int i;
1503
1504    tempEvent = event = *eventP;
1505
1506    if (event->event.lateModifiers)
1507        event->event.lateModifiers->ref_count =
1508            (unsigned short) (event->event.lateModifiers->ref_count + reps - 1);
1509
1510    for (i = 1; i < reps; i++) {
1511        event->next = XtNew(EventSeqRec);
1512        event = event->next;
1513        *event = *tempEvent;
1514    }
1515
1516    event->next = event;
1517    *eventP = event;
1518    *actionsP = &event->actions;
1519}
1520
1521static void
1522RepeatEvent(EventPtr *eventP, int reps, Boolean plus, ActionPtr **actionsP)
1523{
1524    switch ((*eventP)->event.eventType) {
1525
1526    case ButtonPress:
1527    case KeyPress:
1528        if (plus)
1529            RepeatDownPlus(eventP, reps, actionsP);
1530        else
1531            RepeatDown(eventP, reps, actionsP);
1532        break;
1533
1534    case ButtonRelease:
1535    case KeyRelease:
1536        if (plus)
1537            RepeatUpPlus(eventP, reps, actionsP);
1538        else
1539            RepeatUp(eventP, reps, actionsP);
1540        break;
1541
1542    default:
1543        if (plus)
1544            RepeatOtherPlus(eventP, reps, actionsP);
1545        else
1546            RepeatOther(eventP, reps, actionsP);
1547    }
1548}
1549
1550static String
1551ParseRepeat(register String str, int *reps, Boolean *plus, Boolean *error)
1552{
1553
1554    /*** Parse the repetitions, for double click etc... ***/
1555    if (*str != '(' ||
1556        !(isdigit((unsigned char) str[1]) || str[1] == '+' || str[1] == ')'))
1557        return str;
1558    str++;
1559    if (isdigit((unsigned char) *str)) {
1560        String start = str;
1561        char repStr[7];
1562        size_t len;
1563
1564        ScanNumeric(str);
1565        len = (size_t) (str - start);
1566        if (len < sizeof repStr) {
1567            (void) memcpy(repStr, start, len);
1568            repStr[len] = '\0';
1569            *reps = (int) StrToNum(repStr);
1570        }
1571        else {
1572            Syntax("Repeat count too large.", "");
1573            *error = TRUE;
1574            return str;
1575        }
1576    }
1577    if (*reps == 0) {
1578        Syntax("Missing repeat count.", "");
1579        *error = True;
1580        return str;
1581    }
1582
1583    if (*str == '+') {
1584        *plus = TRUE;
1585        str++;
1586    }
1587    if (*str == ')')
1588        str++;
1589    else {
1590        Syntax("Missing ')'.", "");
1591        *error = TRUE;
1592    }
1593
1594    return str;
1595}
1596
1597/***********************************************************************
1598 * ParseEventSeq
1599 * Parses the left hand side of a translation table production
1600 * up to, and consuming the ":".
1601 * Takes a pointer to a char* (where to start parsing) and returns an
1602 * event seq (in a passed in variable), having updated the String
1603 **********************************************************************/
1604
1605static String
1606ParseEventSeq(register String str,
1607              EventSeqPtr *eventSeqP,
1608              ActionPtr ** actionsP,
1609              Boolean *error)
1610{
1611    EventSeqPtr *nextEvent = eventSeqP;
1612
1613    *eventSeqP = NULL;
1614
1615    while (*str != '\0' && !IsNewline(*str)) {
1616        static Event nullEvent =
1617            { 0, 0, NULL, 0, 0L, 0L, _XtRegularMatch, FALSE };
1618        EventPtr event;
1619
1620        ScanWhitespace(str);
1621
1622        if (*str == '"') {
1623            str++;
1624            while (*str != '"' && *str != '\0' && !IsNewline(*str)) {
1625                event = XtNew(EventRec);
1626                event->event = nullEvent;
1627                event->state = /* (StatePtr) -1 */ NULL;
1628                event->next = NULL;
1629                event->actions = NULL;
1630                str = ParseQuotedStringEvent(str, event, error);
1631                if (*error) {
1632                    XtWarningMsg(XtNtranslationParseError, "nonLatin1",
1633                                 XtCXtToolkitError,
1634                                 "... probably due to non-Latin1 character in quoted string",
1635                                 (String *) NULL, (Cardinal *) NULL);
1636                    XtFree((char *) event);
1637                    return PanicModeRecovery(str);
1638                }
1639                *nextEvent = event;
1640                *actionsP = &event->actions;
1641                nextEvent = &event->next;
1642            }
1643            if (*str != '"') {
1644                Syntax("Missing '\"'.", "");
1645                *error = TRUE;
1646                return PanicModeRecovery(str);
1647            }
1648            else
1649                str++;
1650        }
1651        else {
1652            int reps = 0;
1653            Boolean plus = FALSE;
1654
1655            event = XtNew(EventRec);
1656            event->event = nullEvent;
1657            event->state = /* (StatePtr) -1 */ NULL;
1658            event->next = NULL;
1659            event->actions = NULL;
1660
1661            str = ParseEvent(str, event, &reps, &plus, error);
1662            if (*error)
1663                return str;
1664            *nextEvent = event;
1665            *actionsP = &event->actions;
1666            if (reps > 1 || plus)
1667                RepeatEvent(&event, reps, plus, actionsP);
1668            nextEvent = &event->next;
1669        }
1670        ScanWhitespace(str);
1671        if (*str == ':')
1672            break;
1673        else {
1674            if (*str != ',') {
1675                Syntax("',' or ':' expected while parsing event sequence.", "");
1676                *error = TRUE;
1677                return PanicModeRecovery(str);
1678            }
1679            else
1680                str++;
1681        }
1682    }
1683
1684    if (*str != ':') {
1685        Syntax("Missing ':'after event sequence.", "");
1686        *error = TRUE;
1687        return PanicModeRecovery(str);
1688    }
1689    else
1690        str++;
1691
1692    return str;
1693}
1694
1695static String
1696ParseActionProc(register String str, XrmQuark *actionProcNameP, Boolean *error)
1697{
1698    register String start = str;
1699    char procName[200];
1700
1701    str = ScanIdent(str);
1702    if (str - start >= 199) {
1703        Syntax("Action procedure name is longer than 199 chars", "");
1704        *error = TRUE;
1705        return str;
1706    }
1707    (void) memcpy(procName, start, (size_t) (str - start));
1708    procName[str - start] = '\0';
1709    *actionProcNameP = XrmStringToQuark(procName);
1710    return str;
1711}
1712
1713static String
1714ParseString(register String str, _XtString *strP)
1715{
1716    register String start;
1717
1718    if (*str == '"') {
1719        register unsigned prev_len, len;
1720
1721        str++;
1722        start = str;
1723        *strP = NULL;
1724        prev_len = 0;
1725
1726        while (*str != '"' && *str != '\0') {
1727            /* \"  produces double quote embedded in a quoted parameter
1728             * \\" produces backslash as last character of a quoted parameter
1729             */
1730            if (*str == '\\' &&
1731                (*(str + 1) == '"' ||
1732                 (*(str + 1) == '\\' && *(str + 2) == '"'))) {
1733                len = (unsigned) (prev_len + (str - start + 2));
1734                *strP = XtRealloc(*strP, len);
1735                (void) memcpy(*strP + prev_len, start, (size_t) (str - start));
1736                prev_len = len - 1;
1737                str++;
1738                (*strP)[prev_len - 1] = *str;
1739                (*strP)[prev_len] = '\0';
1740                start = str + 1;
1741            }
1742            str++;
1743        }
1744        len = (unsigned) (prev_len + (str - start + 1));
1745        *strP = XtRealloc(*strP, len);
1746        (void) memcpy(*strP + prev_len, start, (size_t) (str - start));
1747        (*strP)[len - 1] = '\0';
1748        if (*str == '"')
1749            str++;
1750        else
1751            XtWarningMsg(XtNtranslationParseError, "parseString",
1752                         XtCXtToolkitError, "Missing '\"'.",
1753                         (String *) NULL, (Cardinal *) NULL);
1754    }
1755    else {
1756        /* scan non-quoted string, stop on whitespace, ',' or ')' */
1757        start = str;
1758        while (*str != ' '
1759               && *str != '\t' && *str != ',' && *str != ')' && !IsNewline(*str)
1760               && *str != '\0')
1761            str++;
1762        *strP = __XtMalloc((unsigned) (str - start + 1));
1763        (void) memcpy(*strP, start, (size_t) (str - start));
1764        (*strP)[str - start] = '\0';
1765    }
1766    return str;
1767}
1768
1769static String
1770ParseParamSeq(register String str, String **paramSeqP, Cardinal *paramNumP)
1771{
1772    typedef struct _ParamRec *ParamPtr;
1773    typedef struct _ParamRec {
1774        ParamPtr next;
1775        String param;
1776    } ParamRec;
1777
1778    ParamPtr params = NULL;
1779    Cardinal num_params = 0;
1780
1781    ScanWhitespace(str);
1782    while (*str != ')' && *str != '\0' && !IsNewline(*str)) {
1783        _XtString newStr;
1784
1785        str = ParseString(str, &newStr);
1786        if (newStr != NULL) {
1787            ParamPtr temp = (ParamRec *)
1788                ALLOCATE_LOCAL((unsigned) sizeof(ParamRec));
1789
1790            if (temp == NULL)
1791                _XtAllocError(NULL);
1792
1793            num_params++;
1794            temp->next = params;
1795            params = temp;
1796            temp->param = newStr;
1797            ScanWhitespace(str);
1798            if (*str == ',') {
1799                str++;
1800                ScanWhitespace(str);
1801            }
1802        }
1803    }
1804
1805    if (num_params != 0) {
1806        String *paramP = XtMallocArray(num_params + 1, (Cardinal)sizeof(String));
1807        Cardinal i;
1808
1809        *paramSeqP = paramP;
1810        *paramNumP = num_params;
1811        paramP += num_params;   /* list is LIFO right now */
1812        *paramP-- = NULL;
1813        for (i = 0; i < num_params; i++) {
1814            ParamPtr next = params->next;
1815
1816            *paramP-- = params->param;
1817            DEALLOCATE_LOCAL((char *) params);
1818            params = next;
1819        }
1820    }
1821    else {
1822        *paramSeqP = NULL;
1823        *paramNumP = 0;
1824    }
1825
1826    return str;
1827}
1828
1829static String
1830ParseAction(String str, ActionPtr actionP, XrmQuark *quarkP, Boolean *error)
1831{
1832    str = ParseActionProc(str, quarkP, error);
1833    if (*error)
1834        return str;
1835
1836    if (*str == '(') {
1837        str++;
1838        str = ParseParamSeq(str, &actionP->params, &actionP->num_params);
1839    }
1840    else {
1841        Syntax("Missing '(' while parsing action sequence", "");
1842        *error = TRUE;
1843        return str;
1844    }
1845    if (*str == ')')
1846        str++;
1847    else {
1848        Syntax("Missing ')' while parsing action sequence", "");
1849        *error = TRUE;
1850        return str;
1851    }
1852    return str;
1853}
1854
1855static String
1856ParseActionSeq(TMParseStateTree parseTree,
1857               String str,
1858               ActionPtr *actionsP,
1859               Boolean *error)
1860{
1861    ActionPtr *nextActionP;
1862
1863    if ((nextActionP = actionsP) != NULL)
1864        *actionsP = NULL;
1865
1866    while (*str != '\0' && !IsNewline(*str)) {
1867        register ActionPtr action;
1868        XrmQuark quark = NULLQUARK;
1869
1870        action = XtNew(ActionRec);
1871        action->params = NULL;
1872        action->num_params = 0;
1873        action->next = NULL;
1874
1875        str = ParseAction(str, action, &quark, error);
1876        if (*error) {
1877            XtFree((char *) action);
1878            return PanicModeRecovery(str);
1879        }
1880
1881        action->idx = _XtGetQuarkIndex(parseTree, quark);
1882        ScanWhitespace(str);
1883        if (nextActionP) {
1884            *nextActionP = action;
1885            nextActionP = &action->next;
1886        }
1887    }
1888    if (IsNewline(*str))
1889        str++;
1890    ScanWhitespace(str);
1891    return str;
1892}
1893
1894static void
1895ShowProduction(String currentProduction)
1896{
1897    Cardinal num_params = 1;
1898    String params[1];
1899    size_t len;
1900    char *eol, *production, productionbuf[500];
1901
1902    eol = strchr(currentProduction, '\n');
1903    if (eol)
1904        len = (size_t) (eol - currentProduction);
1905    else
1906        len = strlen(currentProduction);
1907    production = XtStackAlloc(len + 1, productionbuf);
1908    if (production == NULL)
1909        _XtAllocError(NULL);
1910    (void) memcpy(production, currentProduction, len);
1911    production[len] = '\0';
1912
1913    params[0] = production;
1914    XtWarningMsg(XtNtranslationParseError, "showLine", XtCXtToolkitError,
1915                 "... found while parsing '%s'", params, &num_params);
1916
1917    XtStackFree(production, productionbuf);
1918}
1919
1920/***********************************************************************
1921 * ParseTranslationTableProduction
1922 * Parses one line of event bindings.
1923 ***********************************************************************/
1924
1925static String
1926ParseTranslationTableProduction(TMParseStateTree parseTree,
1927                                register String str,
1928                                Boolean *error)
1929{
1930    EventSeqPtr eventSeq = NULL;
1931    ActionPtr *actionsP;
1932    String production = str;
1933
1934    actionsP = NULL;
1935    str = ParseEventSeq(str, &eventSeq, &actionsP, error);
1936    if (*error == TRUE) {
1937        ShowProduction(production);
1938    }
1939    else {
1940        ScanWhitespace(str);
1941        str = ParseActionSeq(parseTree, str, actionsP, error);
1942        if (*error == TRUE) {
1943            ShowProduction(production);
1944        }
1945        else {
1946            _XtAddEventSeqToStateTree(eventSeq, parseTree);
1947        }
1948    }
1949    FreeEventSeq(eventSeq);
1950    return (str);
1951}
1952
1953static String
1954CheckForPoundSign(String str,
1955                  _XtTranslateOp defaultOp,
1956                  _XtTranslateOp *actualOpRtn)
1957{
1958    _XtTranslateOp opType;
1959
1960    opType = defaultOp;
1961    ScanWhitespace(str);
1962
1963    if (*str == '#') {
1964        String start;
1965        char operation[20];
1966        int len;
1967
1968        str++;
1969        start = str;
1970        str = ScanIdent(str);
1971        len = MIN(19, (int) (str - start));
1972        (void) memcpy(operation, start, (size_t) len);
1973        operation[len] = '\0';
1974        if (!strcmp(operation, "replace"))
1975            opType = XtTableReplace;
1976        else if (!strcmp(operation, "augment"))
1977            opType = XtTableAugment;
1978        else if (!strcmp(operation, "override"))
1979            opType = XtTableOverride;
1980        ScanWhitespace(str);
1981        if (IsNewline(*str)) {
1982            str++;
1983            ScanWhitespace(str);
1984        }
1985    }
1986    *actualOpRtn = opType;
1987    return str;
1988}
1989
1990static XtTranslations
1991ParseTranslationTable(String source,
1992                      Boolean isAccelerator,
1993                      _XtTranslateOp defaultOp,
1994                      Boolean *error)
1995{
1996    XtTranslations xlations;
1997    TMStateTree stateTrees[8];
1998    TMParseStateTreeRec parseTreeRec, *parseTree = &parseTreeRec;
1999    XrmQuark stackQuarks[200];
2000    TMBranchHeadRec stackBranchHeads[200];
2001    StatePtr stackComplexBranchHeads[200];
2002    _XtTranslateOp actualOp;
2003
2004    if (source == NULL)
2005        return (XtTranslations) NULL;
2006
2007    source = CheckForPoundSign(source, defaultOp, &actualOp);
2008    if (isAccelerator && actualOp == XtTableReplace)
2009        actualOp = defaultOp;
2010
2011    parseTree->isSimple = TRUE;
2012    parseTree->mappingNotifyInterest = FALSE;
2013    XtSetBit(parseTree->isAccelerator, isAccelerator);
2014    parseTree->isStackBranchHeads =
2015        parseTree->isStackQuarks = parseTree->isStackComplexBranchHeads = TRUE;
2016
2017    parseTree->numQuarks =
2018        parseTree->numBranchHeads = parseTree->numComplexBranchHeads = 0;
2019
2020    parseTree->quarkTblSize =
2021        parseTree->branchHeadTblSize =
2022        parseTree->complexBranchHeadTblSize = 200;
2023
2024    parseTree->quarkTbl = stackQuarks;
2025    parseTree->branchHeadTbl = stackBranchHeads;
2026    parseTree->complexBranchHeadTbl = stackComplexBranchHeads;
2027
2028    while (source != NULL && *source != '\0') {
2029        source = ParseTranslationTableProduction(parseTree, source, error);
2030        if (*error == TRUE)
2031            break;
2032    }
2033    stateTrees[0] = _XtParseTreeToStateTree(parseTree);
2034
2035    if (!parseTree->isStackQuarks)
2036        XtFree((char *) parseTree->quarkTbl);
2037    if (!parseTree->isStackBranchHeads)
2038        XtFree((char *) parseTree->branchHeadTbl);
2039    if (!parseTree->isStackComplexBranchHeads)
2040        XtFree((char *) parseTree->complexBranchHeadTbl);
2041
2042    xlations = _XtCreateXlations(stateTrees, 1, NULL, NULL);
2043    xlations->operation = (unsigned char) actualOp;
2044
2045#ifdef notdef
2046    XtFree(stateTrees);
2047#endif                          /* notdef */
2048    return xlations;
2049}
2050
2051/*** public procedures ***/
2052
2053Boolean
2054XtCvtStringToAcceleratorTable(Display *dpy,
2055                              XrmValuePtr args _X_UNUSED,
2056                              Cardinal *num_args,
2057                              XrmValuePtr from,
2058                              XrmValuePtr to,
2059                              XtPointer *closure _X_UNUSED)
2060{
2061    String str;
2062    Boolean error = FALSE;
2063
2064    if (*num_args != 0)
2065        XtAppWarningMsg(XtDisplayToApplicationContext(dpy),
2066                        "wrongParameters", "cvtStringToAcceleratorTable",
2067                        XtCXtToolkitError,
2068                        "String to AcceleratorTable conversion needs no extra arguments",
2069                        (String *) NULL, (Cardinal *) NULL);
2070    str = (String) (from->addr);
2071    if (str == NULL) {
2072        XtAppWarningMsg(XtDisplayToApplicationContext(dpy),
2073                        "badParameters", "cvtStringToAcceleratorTable",
2074                        XtCXtToolkitError,
2075                        "String to AcceleratorTable conversion needs string",
2076                        (String *) NULL, (Cardinal *) NULL);
2077        return FALSE;
2078    }
2079    if (to->addr != NULL) {
2080        if (to->size < sizeof(XtAccelerators)) {
2081            to->size = sizeof(XtAccelerators);
2082            return FALSE;
2083        }
2084        *(XtAccelerators *) to->addr =
2085            (XtAccelerators) ParseTranslationTable(str, TRUE, XtTableAugment,
2086                                                   &error);
2087    }
2088    else {
2089        static XtAccelerators staticStateTable;
2090
2091        staticStateTable =
2092            (XtAccelerators) ParseTranslationTable(str, TRUE, XtTableAugment,
2093                                                   &error);
2094        to->addr = (XPointer) &staticStateTable;
2095        to->size = sizeof(XtAccelerators);
2096    }
2097    if (error == TRUE)
2098        XtAppWarningMsg(XtDisplayToApplicationContext(dpy),
2099                        "parseError", "cvtStringToAcceleratorTable",
2100                        XtCXtToolkitError,
2101                        "String to AcceleratorTable conversion encountered errors",
2102                        (String *) NULL, (Cardinal *) NULL);
2103    return (error != TRUE);
2104}
2105
2106Boolean
2107XtCvtStringToTranslationTable(Display *dpy,
2108                              XrmValuePtr args _X_UNUSED,
2109                              Cardinal *num_args,
2110                              XrmValuePtr from,
2111                              XrmValuePtr to,
2112                              XtPointer *closure_ret _X_UNUSED)
2113{
2114    String str;
2115    Boolean error = FALSE;
2116
2117    if (*num_args != 0)
2118        XtAppWarningMsg(XtDisplayToApplicationContext(dpy),
2119                        "wrongParameters", "cvtStringToTranslationTable",
2120                        XtCXtToolkitError,
2121                        "String to TranslationTable conversion needs no extra arguments",
2122                        (String *) NULL, (Cardinal *) NULL);
2123    str = (String) (from->addr);
2124    if (str == NULL) {
2125        XtAppWarningMsg(XtDisplayToApplicationContext(dpy),
2126                        "badParameters", "cvtStringToTranslation",
2127                        XtCXtToolkitError,
2128                        "String to TranslationTable conversion needs string",
2129                        (String *) NULL, (Cardinal *) NULL);
2130        return FALSE;
2131    }
2132    if (to->addr != NULL) {
2133        if (to->size < sizeof(XtTranslations)) {
2134            to->size = sizeof(XtTranslations);
2135            return FALSE;
2136        }
2137        *(XtTranslations *) to->addr =
2138            ParseTranslationTable(str, FALSE, XtTableReplace, &error);
2139    }
2140    else {
2141        static XtTranslations staticStateTable;
2142
2143        staticStateTable =
2144            ParseTranslationTable(str, FALSE, XtTableReplace, &error);
2145        to->addr = (XPointer) &staticStateTable;
2146        to->size = sizeof(XtTranslations);
2147    }
2148    if (error == TRUE)
2149        XtAppWarningMsg(XtDisplayToApplicationContext(dpy),
2150                        "parseError", "cvtStringToTranslationTable",
2151                        XtCXtToolkitError,
2152                        "String to TranslationTable conversion encountered errors",
2153                        (String *) NULL, (Cardinal *) NULL);
2154    return (error != TRUE);
2155}
2156
2157/*
2158 * Parses a user's or applications translation table
2159 */
2160XtAccelerators
2161XtParseAcceleratorTable(_Xconst char *source)
2162{
2163    Boolean error = FALSE;
2164    XtAccelerators ret =
2165        (XtAccelerators) ParseTranslationTable(source, TRUE, XtTableAugment,
2166                                               &error);
2167
2168    if (error == TRUE)
2169        XtWarningMsg("parseError", "cvtStringToAcceleratorTable",
2170                     XtCXtToolkitError,
2171                     "String to AcceleratorTable conversion encountered errors",
2172                     (String *) NULL, (Cardinal *) NULL);
2173    return ret;
2174}
2175
2176XtTranslations
2177XtParseTranslationTable(_Xconst char *source)
2178{
2179    Boolean error = FALSE;
2180    XtTranslations ret =
2181        ParseTranslationTable(source, FALSE, XtTableReplace, &error);
2182    if (error == TRUE)
2183        XtWarningMsg("parseError",
2184                     "cvtStringToTranslationTable", XtCXtToolkitError,
2185                     "String to TranslationTable conversion encountered errors",
2186                     (String *) NULL, (Cardinal *) NULL);
2187    return ret;
2188}
2189
2190void
2191_XtTranslateInitialize(void)
2192{
2193    LOCK_PROCESS;
2194    if (initialized) {
2195        XtWarningMsg("translationError", "xtTranslateInitialize",
2196                     XtCXtToolkitError,
2197                     "Initializing Translation manager twice.", (String *) NULL,
2198                     (Cardinal *) NULL);
2199        UNLOCK_PROCESS;
2200        return;
2201    }
2202
2203    initialized = TRUE;
2204    UNLOCK_PROCESS;
2205    QMeta = XrmPermStringToQuark("Meta");
2206    QCtrl = XrmPermStringToQuark("Ctrl");
2207    QNone = XrmPermStringToQuark("None");
2208    QAny = XrmPermStringToQuark("Any");
2209
2210    Compile_XtEventTable(events, XtNumber(events));
2211    Compile_XtModifierTable(modifiers, XtNumber(modifiers));
2212    CompileNameValueTable(notifyModes);
2213    CompileNameValueTable(motionDetails);
2214#if 0
2215    CompileNameValueTable(notifyDetail);
2216    CompileNameValueTable(visibilityNotify);
2217    CompileNameValueTable(circulation);
2218    CompileNameValueTable(propertyChanged);
2219#endif
2220    CompileNameValueTable(mappingNotify);
2221}
2222
2223void
2224_XtAddTMConverters(ConverterTable table)
2225{
2226    _XtTableAddConverter(table,
2227                         _XtQString,
2228                         XrmPermStringToQuark(XtRTranslationTable),
2229                         XtCvtStringToTranslationTable, (XtConvertArgList) NULL,
2230                         (Cardinal) 0, True, CACHED, _XtFreeTranslations, True);
2231    _XtTableAddConverter(table, _XtQString,
2232                         XrmPermStringToQuark(XtRAcceleratorTable),
2233                         XtCvtStringToAcceleratorTable, (XtConvertArgList) NULL,
2234                         (Cardinal) 0, True, CACHED, _XtFreeTranslations, True);
2235    _XtTableAddConverter(table,
2236                         XrmPermStringToQuark(_XtRStateTablePair),
2237                         XrmPermStringToQuark(XtRTranslationTable),
2238                         _XtCvtMergeTranslations, (XtConvertArgList) NULL,
2239                         (Cardinal) 0, True, CACHED, _XtFreeTranslations, True);
2240}
2241