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