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