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