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