1 /***********************************************************
2 Copyright (c) 1993, Oracle and/or its affiliates. All rights reserved.
3 
4 Permission is hereby granted, free of charge, to any person obtaining a
5 copy of this software and associated documentation files (the "Software"),
6 to deal in the Software without restriction, including without limitation
7 the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 and/or sell copies of the Software, and to permit persons to whom the
9 Software is furnished to do so, subject to the following conditions:
10 
11 The above copyright notice and this permission notice (including the next
12 paragraph) shall be included in all copies or substantial portions of the
13 Software.
14 
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 DEALINGS IN THE SOFTWARE.
22 
23 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
24 
25                         All Rights Reserved
26 
27 Permission to use, copy, modify, and distribute this software and its
28 documentation for any purpose and without fee is hereby granted,
29 provided that the above copyright notice appear in all copies and that
30 both that copyright notice and this permission notice appear in
31 supporting documentation, and that the name of Digital not be
32 used in advertising or publicity pertaining to distribution of the
33 software without specific, written prior permission.
34 
35 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
36 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
37 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
38 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
39 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
40 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
41 SOFTWARE.
42 
43 ******************************************************************/
44 
45 /*
46 
47 Copyright 1987, 1988, 1994, 1998  The Open Group
48 
49 Permission to use, copy, modify, distribute, and sell this software and its
50 documentation for any purpose is hereby granted without fee, provided that
51 the above copyright notice appear in all copies and that both that
52 copyright notice and this permission notice appear in supporting
53 documentation.
54 
55 The above copyright notice and this permission notice shall be included in
56 all copies or substantial portions of the Software.
57 
58 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
59 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
60 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
61 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
62 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
63 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
64 
65 Except as contained in this notice, the name of The Open Group shall not be
66 used in advertising or otherwise to promote the sale, use or other dealings
67 in this Software without prior written authorization from The Open Group.
68 
69 */
70 
71 /* TMstate.c -- maintains the state table of actions for the translation
72  *              manager.
73  */
74 #ifdef HAVE_CONFIG_H
75 #include <config.h>
76 #endif
77 #include "IntrinsicI.h"
78 #ifndef TM_NO_MATCH
79 #define TM_NO_MATCH (-2)
80 #endif                          /* TM_NO_MATCH */
81 /* forward definitions */
82 static StatePtr NewState(TMParseStateTree, TMShortCard, TMShortCard);
83 
84 static String XtNtranslationError = "translationError";
85 
86 #ifndef __EMX__
87 TMGlobalRec _XtGlobalTM;        /* initialized to zero K&R */
88 #else
89 TMGlobalRec _XtGlobalTM = { 0 };
90 #endif
91 
92 #define MatchIncomingEvent(tmEvent, typeMatch, modMatch) \
93   (typeMatch->eventType == tmEvent->event.eventType && \
94    (typeMatch->matchEvent != NULL) && \
95    (*typeMatch->matchEvent)(typeMatch, modMatch, tmEvent))
96 
97 #define NumStateTrees(xlations) \
98   ((translateData->isSimple) ? 1 : (TMComplexXlations(xlations))->numTrees)
99 
100 static TMShortCard
GetBranchHead(TMParseStateTree parseTree,TMShortCard typeIndex,TMShortCard modIndex,Boolean isDummy)101 GetBranchHead(TMParseStateTree parseTree,
102               TMShortCard typeIndex,
103               TMShortCard modIndex,
104               Boolean isDummy)
105 {
106 #define TM_BRANCH_HEAD_TBL_ALLOC        8
107 #define TM_BRANCH_HEAD_TBL_REALLOC      8
108 
109     TMBranchHead branchHead = parseTree->branchHeadTbl;
110 
111     /*
112      * dummy is used as a place holder for later matching in old-style
113      * matching behavior. If there's already an entry we don't need
114      * another dummy.
115      */
116     if (isDummy) {
117         TMShortCard i;
118 
119         for (i = 0; i < parseTree->numBranchHeads; i++, branchHead++) {
120             if ((branchHead->typeIndex == typeIndex) &&
121                 (branchHead->modIndex == modIndex))
122                 return i;
123         }
124     }
125     if (parseTree->numBranchHeads == parseTree->branchHeadTblSize) {
126         TMShortCard newSize;
127 
128         if (parseTree->branchHeadTblSize == 0)
129             parseTree->branchHeadTblSize =
130                 (TMShortCard) (parseTree->branchHeadTblSize +
131                                TM_BRANCH_HEAD_TBL_ALLOC);
132         else
133             parseTree->branchHeadTblSize =
134                 (TMShortCard) (parseTree->branchHeadTblSize +
135                                TM_BRANCH_HEAD_TBL_REALLOC);
136         newSize =
137             (TMShortCard) (parseTree->branchHeadTblSize *
138                            sizeof(TMBranchHeadRec));
139         if (parseTree->isStackBranchHeads) {
140             TMBranchHead oldBranchHeadTbl = parseTree->branchHeadTbl;
141 
142             parseTree->branchHeadTbl = (TMBranchHead) __XtMalloc(newSize);
143             XtMemmove(parseTree->branchHeadTbl, oldBranchHeadTbl, newSize);
144             parseTree->isStackBranchHeads = False;
145         }
146         else {
147             parseTree->branchHeadTbl = (TMBranchHead)
148                 XtRealloc((char *) parseTree->branchHeadTbl,
149                           (Cardinal) (parseTree->branchHeadTblSize *
150                                       sizeof(TMBranchHeadRec)));
151         }
152     }
153 #ifdef TRACE_TM
154     LOCK_PROCESS;
155     _XtGlobalTM.numBranchHeads++;
156     UNLOCK_PROCESS;
157 #endif                          /* TRACE_TM */
158     branchHead = &parseTree->branchHeadTbl[parseTree->numBranchHeads++];
159     branchHead->typeIndex = typeIndex;
160     branchHead->modIndex = modIndex;
161     branchHead->more = 0;
162     branchHead->isSimple = True;
163     branchHead->hasActions = False;
164     branchHead->hasCycles = False;
165     return (TMShortCard) (parseTree->numBranchHeads - 1);
166 }
167 
168 TMShortCard
_XtGetQuarkIndex(TMParseStateTree parseTree,XrmQuark quark)169 _XtGetQuarkIndex(TMParseStateTree parseTree, XrmQuark quark)
170 {
171 #define TM_QUARK_TBL_ALLOC      16
172 #define TM_QUARK_TBL_REALLOC    16
173     TMShortCard i;
174 
175     for (i = 0; i < parseTree->numQuarks; i++)
176         if (parseTree->quarkTbl[i] == quark)
177             break;
178 
179     if (i == parseTree->numQuarks) {
180         if (parseTree->numQuarks == parseTree->quarkTblSize) {
181             TMShortCard newSize;
182 
183             if (parseTree->quarkTblSize == 0)
184                 parseTree->quarkTblSize =
185                     (TMShortCard) (parseTree->quarkTblSize +
186                                    TM_QUARK_TBL_ALLOC);
187             else
188                 parseTree->quarkTblSize =
189                     (TMShortCard) (parseTree->quarkTblSize +
190                                    TM_QUARK_TBL_REALLOC);
191             newSize =
192                 (TMShortCard) (parseTree->quarkTblSize * sizeof(XrmQuark));
193 
194             if (parseTree->isStackQuarks) {
195                 XrmQuark *oldquarkTbl = parseTree->quarkTbl;
196 
197                 parseTree->quarkTbl = (XrmQuark *) __XtMalloc(newSize);
198                 XtMemmove(parseTree->quarkTbl, oldquarkTbl, newSize);
199                 parseTree->isStackQuarks = False;
200             }
201             else {
202                 parseTree->quarkTbl = (XrmQuark *)
203                     XtRealloc((char *) parseTree->quarkTbl,
204                               (Cardinal) (parseTree->quarkTblSize *
205                                           sizeof(XrmQuark)));
206             }
207         }
208         parseTree->quarkTbl[parseTree->numQuarks++] = quark;
209     }
210     return i;
211 }
212 
213 /*
214  * Get an entry from the parseTrees complex branchHead tbl. If there's none
215  * there then allocate one
216  */
217 static TMShortCard
GetComplexBranchIndex(TMParseStateTree parseTree,TMShortCard typeIndex _X_UNUSED,TMShortCard modIndex _X_UNUSED)218 GetComplexBranchIndex(TMParseStateTree parseTree,
219                       TMShortCard typeIndex _X_UNUSED,
220                       TMShortCard modIndex _X_UNUSED)
221 {
222 #define TM_COMPLEXBRANCH_HEAD_TBL_ALLOC 8
223 #define TM_COMPLEXBRANCH_HEAD_TBL_REALLOC 4
224 
225     if (parseTree->numComplexBranchHeads == parseTree->complexBranchHeadTblSize) {
226         TMShortCard newSize;
227 
228         if (parseTree->complexBranchHeadTblSize == 0)
229             parseTree->complexBranchHeadTblSize =
230                 (TMShortCard) (parseTree->complexBranchHeadTblSize +
231                                TM_COMPLEXBRANCH_HEAD_TBL_ALLOC);
232         else
233             parseTree->complexBranchHeadTblSize =
234                 (TMShortCard) (parseTree->complexBranchHeadTblSize +
235                                TM_COMPLEXBRANCH_HEAD_TBL_REALLOC);
236 
237         newSize =
238             (TMShortCard) (parseTree->complexBranchHeadTblSize *
239                            sizeof(StatePtr));
240 
241         if (parseTree->isStackComplexBranchHeads) {
242             StatePtr *oldcomplexBranchHeadTbl = parseTree->complexBranchHeadTbl;
243 
244             parseTree->complexBranchHeadTbl = (StatePtr *) __XtMalloc(newSize);
245             XtMemmove(parseTree->complexBranchHeadTbl,
246                       oldcomplexBranchHeadTbl, newSize);
247             parseTree->isStackComplexBranchHeads = False;
248         }
249         else {
250             parseTree->complexBranchHeadTbl = (StatePtr *)
251                 XtRealloc((char *) parseTree->complexBranchHeadTbl,
252                           (Cardinal) (parseTree->complexBranchHeadTblSize *
253                                       sizeof(StatePtr)));
254         }
255     }
256     parseTree->complexBranchHeadTbl[parseTree->numComplexBranchHeads++] = NULL;
257     return (TMShortCard) (parseTree->numComplexBranchHeads - 1);
258 }
259 
260 TMShortCard
_XtGetTypeIndex(Event * event)261 _XtGetTypeIndex(Event *event)
262 {
263     TMShortCard i, j = TM_TYPE_SEGMENT_SIZE;
264     TMShortCard typeIndex = 0;
265     TMTypeMatch typeMatch;
266     TMTypeMatch segment = NULL;
267 
268     LOCK_PROCESS;
269     for (i = 0; i < _XtGlobalTM.numTypeMatchSegments; i++) {
270         segment = _XtGlobalTM.typeMatchSegmentTbl[i];
271         for (j = 0;
272              typeIndex < _XtGlobalTM.numTypeMatches && j < TM_TYPE_SEGMENT_SIZE;
273              j++, typeIndex++) {
274             typeMatch = &(segment[j]);
275             if (event->eventType == typeMatch->eventType &&
276                 event->eventCode == typeMatch->eventCode &&
277                 event->eventCodeMask == typeMatch->eventCodeMask &&
278                 event->matchEvent == typeMatch->matchEvent) {
279                 UNLOCK_PROCESS;
280                 return typeIndex;
281             }
282         }
283     }
284 
285     if (j == TM_TYPE_SEGMENT_SIZE) {
286         if (_XtGlobalTM.numTypeMatchSegments ==
287             _XtGlobalTM.typeMatchSegmentTblSize) {
288             _XtGlobalTM.typeMatchSegmentTblSize =
289                 (TMShortCard) (_XtGlobalTM.typeMatchSegmentTblSize + 4);
290             _XtGlobalTM.typeMatchSegmentTbl = (TMTypeMatch *)
291                 XtRealloc((char *) _XtGlobalTM.typeMatchSegmentTbl,
292                           (Cardinal) (_XtGlobalTM.typeMatchSegmentTblSize *
293                                       sizeof(TMTypeMatch)));
294         }
295         _XtGlobalTM.typeMatchSegmentTbl[_XtGlobalTM.numTypeMatchSegments++] =
296             segment = (TMTypeMatch)
297             __XtMalloc(TM_TYPE_SEGMENT_SIZE * sizeof(TMTypeMatchRec));
298         j = 0;
299     }
300     typeMatch = &segment[j];
301     typeMatch->eventType = event->eventType;
302     typeMatch->eventCode = event->eventCode;
303     typeMatch->eventCodeMask = event->eventCodeMask;
304     typeMatch->matchEvent = event->matchEvent;
305     _XtGlobalTM.numTypeMatches++;
306     UNLOCK_PROCESS;
307     return typeIndex;
308 }
309 
310 static Boolean
CompareLateModifiers(LateBindingsPtr lateBind1P,LateBindingsPtr lateBind2P)311 CompareLateModifiers(LateBindingsPtr lateBind1P, LateBindingsPtr lateBind2P)
312 {
313     LateBindingsPtr late1P = lateBind1P;
314     LateBindingsPtr late2P = lateBind2P;
315 
316     if (late1P != NULL || late2P != NULL) {
317         int i = 0;
318         int j = 0;
319 
320         if (late1P != NULL)
321             for (; late1P->keysym != NoSymbol; i++)
322                 late1P++;
323         if (late2P != NULL)
324             for (; late2P->keysym != NoSymbol; j++)
325                 late2P++;
326         if (i != j)
327             return FALSE;
328         late1P--;
329         while (late1P >= lateBind1P) {
330             Boolean last = True;
331 
332             for (late2P = lateBind2P + i - 1; late2P >= lateBind2P; late2P--) {
333                 if (late1P->keysym == late2P->keysym
334                     && late1P->knot == late2P->knot) {
335                     j--;
336                     if (last)
337                         i--;
338                     break;
339                 }
340                 last = False;
341             }
342             late1P--;
343         }
344         if (j != 0)
345             return FALSE;
346     }
347     return TRUE;
348 }
349 
350 TMShortCard
_XtGetModifierIndex(Event * event)351 _XtGetModifierIndex(Event *event)
352 {
353     TMShortCard i, j = TM_MOD_SEGMENT_SIZE;
354     TMShortCard modIndex = 0;
355     TMModifierMatch modMatch;
356     TMModifierMatch segment = NULL;
357 
358     LOCK_PROCESS;
359     for (i = 0; i < _XtGlobalTM.numModMatchSegments; i++) {
360         segment = _XtGlobalTM.modMatchSegmentTbl[i];
361         for (j = 0;
362              modIndex < _XtGlobalTM.numModMatches && j < TM_MOD_SEGMENT_SIZE;
363              j++, modIndex++) {
364             modMatch = &(segment[j]);
365             if (event->modifiers == modMatch->modifiers &&
366                 event->modifierMask == modMatch->modifierMask &&
367                 event->standard == modMatch->standard &&
368                 ((!event->lateModifiers && !modMatch->lateModifiers) ||
369                  CompareLateModifiers(event->lateModifiers,
370                                       modMatch->lateModifiers))) {
371                 /*
372                  * if we found a match then we can free the parser's
373                  * late modifiers. If there isn't a match we use the
374                  * parser's copy
375                  */
376                 if (event->lateModifiers &&
377                     --event->lateModifiers->ref_count == 0) {
378                     XtFree((char *) event->lateModifiers);
379                     event->lateModifiers = NULL;
380                 }
381                 UNLOCK_PROCESS;
382                 return modIndex;
383             }
384         }
385     }
386 
387     if (j == TM_MOD_SEGMENT_SIZE) {
388         if (_XtGlobalTM.numModMatchSegments ==
389             _XtGlobalTM.modMatchSegmentTblSize) {
390             _XtGlobalTM.modMatchSegmentTblSize =
391                 (TMShortCard) (_XtGlobalTM.modMatchSegmentTblSize + 4);
392             _XtGlobalTM.modMatchSegmentTbl = (TMModifierMatch *)
393                 XtRealloc((char *) _XtGlobalTM.modMatchSegmentTbl,
394                           (Cardinal) (_XtGlobalTM.modMatchSegmentTblSize *
395                                       sizeof(TMModifierMatch)));
396         }
397         _XtGlobalTM.modMatchSegmentTbl[_XtGlobalTM.numModMatchSegments++] =
398             segment = (TMModifierMatch)
399             __XtMalloc(TM_MOD_SEGMENT_SIZE * sizeof(TMModifierMatchRec));
400         j = 0;
401     }
402     modMatch = &segment[j];
403     modMatch->modifiers = event->modifiers;
404     modMatch->modifierMask = event->modifierMask;
405     modMatch->standard = event->standard;
406     /*
407      * We use the parser's copy of the late binding array
408      */
409 #ifdef TRACE_TM
410     if (event->lateModifiers)
411         _XtGlobalTM.numLateBindings++;
412 #endif                          /* TRACE_TM */
413     modMatch->lateModifiers = event->lateModifiers;
414     _XtGlobalTM.numModMatches++;
415     UNLOCK_PROCESS;
416     return modIndex;
417 }
418 
419 /*
420  * This is called from the SimpleStateHandler to match a stateTree
421  * entry to the event coming in
422  */
423 static int
MatchBranchHead(TMSimpleStateTree stateTree,int startIndex,TMEventPtr event)424 MatchBranchHead(TMSimpleStateTree stateTree, int startIndex, TMEventPtr event)
425 {
426     TMBranchHead branchHead = &stateTree->branchHeadTbl[startIndex];
427     int i;
428 
429     LOCK_PROCESS;
430     for (i = startIndex; i < (int) stateTree->numBranchHeads; i++, branchHead++) {
431         TMTypeMatch typeMatch;
432         TMModifierMatch modMatch;
433 
434         typeMatch = TMGetTypeMatch(branchHead->typeIndex);
435         modMatch = TMGetModifierMatch(branchHead->modIndex);
436 
437         if (MatchIncomingEvent(event, typeMatch, modMatch)) {
438             UNLOCK_PROCESS;
439             return i;
440         }
441     }
442     UNLOCK_PROCESS;
443     return (TM_NO_MATCH);
444 }
445 
446 Boolean
_XtRegularMatch(TMTypeMatch typeMatch,TMModifierMatch modMatch,TMEventPtr eventSeq)447 _XtRegularMatch(TMTypeMatch typeMatch,
448                 TMModifierMatch modMatch,
449                 TMEventPtr eventSeq)
450 {
451     Modifiers computed = 0;
452     Modifiers computedMask = 0;
453     Boolean resolved = TRUE;
454 
455     if (typeMatch->eventCode != (eventSeq->event.eventCode &
456                                  typeMatch->eventCodeMask))
457         return FALSE;
458     if (modMatch->lateModifiers != NULL)
459         resolved = _XtComputeLateBindings(eventSeq->xev->xany.display,
460                                           modMatch->lateModifiers,
461                                           &computed, &computedMask);
462     if (!resolved)
463         return FALSE;
464     computed = (Modifiers) (computed | modMatch->modifiers);
465     computedMask = (Modifiers) (computedMask | modMatch->modifierMask);
466 
467     return ((computed & computedMask) ==
468             (eventSeq->event.modifiers & computedMask));
469 }
470 
471 Boolean
_XtMatchAtom(TMTypeMatch typeMatch,TMModifierMatch modMatch _X_UNUSED,TMEventPtr eventSeq)472 _XtMatchAtom(TMTypeMatch typeMatch,
473              TMModifierMatch modMatch _X_UNUSED,
474              TMEventPtr eventSeq)
475 {
476     Atom atom;
477 
478     atom = XInternAtom(eventSeq->xev->xany.display,
479                        XrmQuarkToString((XrmQuark) (typeMatch->eventCode)),
480                        False);
481     return (atom == eventSeq->event.eventCode);
482 }
483 
484 #define IsOn(vec,idx) ((vec)[(idx)>>3] & (1 << ((idx) & 7)))
485 
486 /*
487  * there are certain cases where you want to ignore the event and stay
488  * in the same state.
489  */
490 static Boolean
Ignore(TMEventPtr event)491 Ignore(TMEventPtr event)
492 {
493     Display *dpy;
494     XtPerDisplay pd;
495 
496     if (event->event.eventType == MotionNotify)
497         return TRUE;
498     if (!(event->event.eventType == KeyPress ||
499           event->event.eventType == KeyRelease))
500         return FALSE;
501     dpy = event->xev->xany.display;
502 
503     pd = _XtGetPerDisplay(dpy);
504     _InitializeKeysymTables(dpy, pd);
505     return IsOn(pd->isModifier, event->event.eventCode) ? TRUE : FALSE;
506 }
507 
508 static void
XEventToTMEvent(XEvent * event,TMEventPtr tmEvent)509 XEventToTMEvent(XEvent *event, TMEventPtr tmEvent)
510 {
511     tmEvent->xev = event;
512     tmEvent->event.eventCodeMask = 0;
513     tmEvent->event.modifierMask = 0;
514     tmEvent->event.eventType = (TMLongCard) event->type;
515     tmEvent->event.lateModifiers = NULL;
516     tmEvent->event.matchEvent = NULL;
517     tmEvent->event.standard = FALSE;
518 
519     switch (event->type) {
520 
521     case KeyPress:
522     case KeyRelease:
523         tmEvent->event.eventCode = event->xkey.keycode;
524         tmEvent->event.modifiers = event->xkey.state;
525         break;
526 
527     case ButtonPress:
528     case ButtonRelease:
529         tmEvent->event.eventCode = event->xbutton.button;
530         tmEvent->event.modifiers = event->xbutton.state;
531         break;
532 
533     case MotionNotify:
534         tmEvent->event.eventCode = (TMLongCard) event->xmotion.is_hint;
535         tmEvent->event.modifiers = event->xmotion.state;
536         break;
537 
538     case EnterNotify:
539     case LeaveNotify:
540         tmEvent->event.eventCode = (TMLongCard) event->xcrossing.mode;
541         tmEvent->event.modifiers = event->xcrossing.state;
542         break;
543 
544     case PropertyNotify:
545         tmEvent->event.eventCode = event->xproperty.atom;
546         tmEvent->event.modifiers = 0;
547         break;
548 
549     case SelectionClear:
550         tmEvent->event.eventCode = event->xselectionclear.selection;
551         tmEvent->event.modifiers = 0;
552         break;
553 
554     case SelectionRequest:
555         tmEvent->event.eventCode = event->xselectionrequest.selection;
556         tmEvent->event.modifiers = 0;
557         break;
558 
559     case SelectionNotify:
560         tmEvent->event.eventCode = event->xselection.selection;
561         tmEvent->event.modifiers = 0;
562         break;
563 
564     case ClientMessage:
565         tmEvent->event.eventCode = event->xclient.message_type;
566         tmEvent->event.modifiers = 0;
567         break;
568 
569     case MappingNotify:
570         tmEvent->event.eventCode = (TMLongCard) event->xmapping.request;
571         tmEvent->event.modifiers = 0;
572         break;
573 
574     case FocusIn:
575     case FocusOut:
576         tmEvent->event.eventCode = (TMLongCard) event->xfocus.mode;
577         tmEvent->event.modifiers = 0;
578         break;
579 
580     default:
581         tmEvent->event.eventCode = 0;
582         tmEvent->event.modifiers = 0;
583         break;
584     }
585 }
586 
587 static unsigned long
GetTime(XtTM tm,XEvent * event)588 GetTime(XtTM tm, XEvent *event)
589 {
590     switch (event->type) {
591 
592     case KeyPress:
593     case KeyRelease:
594         return event->xkey.time;
595 
596     case ButtonPress:
597     case ButtonRelease:
598         return event->xbutton.time;
599 
600     default:
601         return tm->lastEventTime;
602 
603     }
604 
605 }
606 
607 static void
HandleActions(Widget w,XEvent * event,TMSimpleStateTree stateTree,Widget accelWidget,XtActionProc * procs,ActionRec * actions)608 HandleActions(Widget w,
609               XEvent *event,
610               TMSimpleStateTree stateTree,
611               Widget accelWidget,
612               XtActionProc *procs,
613               ActionRec *actions)
614 {
615     ActionHook actionHookList;
616     Widget bindWidget;
617 
618     bindWidget = accelWidget ? accelWidget : w;
619     if (accelWidget && !XtIsSensitive(accelWidget) &&
620         (event->type == KeyPress || event->type == KeyRelease ||
621          event->type == ButtonPress || event->type == ButtonRelease ||
622          event->type == MotionNotify || event->type == EnterNotify ||
623          event->type == LeaveNotify || event->type == FocusIn ||
624          event->type == FocusOut))
625         return;
626 
627     actionHookList = XtWidgetToApplicationContext(w)->action_hook_list;
628 
629     while (actions != NULL) {
630         /* perform any actions */
631         if (procs[actions->idx] != NULL) {
632             if (actionHookList) {
633                 ActionHook hook;
634                 ActionHook next_hook;
635                 String procName =
636                     XrmQuarkToString(stateTree->quarkTbl[actions->idx]);
637 
638                 for (hook = actionHookList; hook != NULL;) {
639                     /*
640                      * Need to cache hook->next because the following action
641                      * proc may free hook via XtRemoveActionHook making
642                      * hook->next invalid upon return from the action proc.
643                      */
644                     next_hook = hook->next;
645                     (*hook->proc) (bindWidget,
646                                    hook->closure,
647                                    procName,
648                                    event,
649                                    actions->params, &actions->num_params);
650                     hook = next_hook;
651                 }
652             }
653             (*(procs[actions->idx]))
654                 (bindWidget, event, actions->params, &actions->num_params);
655         }
656         actions = actions->next;
657     }
658 }
659 
660 typedef struct {
661     unsigned int isCycleStart:1;
662     unsigned int isCycleEnd:1;
663     TMShortCard typeIndex;
664     TMShortCard modIndex;
665 } MatchPairRec, *MatchPair;
666 
667 typedef struct TMContextRec {
668     TMShortCard numMatches;
669     TMShortCard maxMatches;
670     MatchPair matches;
671 } TMContextRec, *TMContext;
672 
673 static TMContextRec contextCache[2];
674 
675 #define GetContextPtr(tm) ((TMContext *)&(tm->current_state))
676 
677 #define TM_CONTEXT_MATCHES_ALLOC 4
678 #define TM_CONTEXT_MATCHES_REALLOC 2
679 
680 static void
PushContext(TMContext * contextPtr,StatePtr newState)681 PushContext(TMContext *contextPtr, StatePtr newState)
682 {
683     TMContext context = *contextPtr;
684 
685     LOCK_PROCESS;
686     if (context == NULL) {
687         if (contextCache[0].numMatches == 0)
688             context = &contextCache[0];
689         else if (contextCache[1].numMatches == 0)
690             context = &contextCache[1];
691         if (!context) {
692             context = XtNew(TMContextRec);
693             context->matches = NULL;
694             context->numMatches = context->maxMatches = 0;
695         }
696     }
697     if (context->numMatches &&
698         context->matches[context->numMatches - 1].isCycleEnd) {
699         TMShortCard i;
700 
701         for (i = 0;
702              i < context->numMatches &&
703              !(context->matches[i].isCycleStart); i++) {
704         };
705         if (i < context->numMatches)
706             context->numMatches = (TMShortCard) (i + 1);
707 #ifdef DEBUG
708         else
709             XtWarning("pushing cycle end with no cycle start");
710 #endif                          /* DEBUG */
711     }
712     else {
713         if (context->numMatches == context->maxMatches) {
714             if (context->maxMatches == 0)
715                 context->maxMatches =
716                     (TMShortCard) (context->maxMatches +
717                                    TM_CONTEXT_MATCHES_ALLOC);
718             else
719                 context->maxMatches =
720                     (TMShortCard) (context->maxMatches +
721                                    TM_CONTEXT_MATCHES_REALLOC);
722             context->matches = (MatchPairRec *)
723                 XtRealloc((char *) context->matches,
724                           (Cardinal) (context->maxMatches *
725                                       sizeof(MatchPairRec)));
726         }
727         context->matches[context->numMatches].isCycleStart =
728             newState->isCycleStart;
729         context->matches[context->numMatches].isCycleEnd = newState->isCycleEnd;
730         context->matches[context->numMatches].typeIndex = newState->typeIndex;
731         context->matches[context->numMatches++].modIndex = newState->modIndex;
732         *contextPtr = context;
733     }
734     UNLOCK_PROCESS;
735 }
736 
737 static void
FreeContext(TMContext * contextPtr)738 FreeContext(TMContext *contextPtr)
739 {
740     TMContext context = NULL;
741 
742     LOCK_PROCESS;
743 
744     if (&contextCache[0] == *contextPtr)
745         context = &contextCache[0];
746     else if (&contextCache[1] == *contextPtr)
747         context = &contextCache[1];
748 
749     if (context)
750         context->numMatches = 0;
751     else if (*contextPtr) {
752         XtFree((char *) ((*contextPtr)->matches));
753         XtFree((char *) *contextPtr);
754     }
755 
756     *contextPtr = NULL;
757     UNLOCK_PROCESS;
758 }
759 
760 static int
MatchExact(TMSimpleStateTree stateTree,int startIndex,TMShortCard typeIndex,TMShortCard modIndex)761 MatchExact(TMSimpleStateTree stateTree,
762            int startIndex,
763            TMShortCard typeIndex,
764            TMShortCard modIndex)
765 {
766     TMBranchHead branchHead = &(stateTree->branchHeadTbl[startIndex]);
767     int i;
768 
769     for (i = startIndex; i < (int) stateTree->numBranchHeads; i++, branchHead++) {
770         if ((branchHead->typeIndex == typeIndex) &&
771             (branchHead->modIndex == modIndex))
772             return i;
773     }
774     return (TM_NO_MATCH);
775 }
776 
777 static void
HandleSimpleState(Widget w,XtTM tmRecPtr,TMEventRec * curEventPtr)778 HandleSimpleState(Widget w, XtTM tmRecPtr, TMEventRec *curEventPtr)
779 {
780     XtTranslations xlations = tmRecPtr->translations;
781     TMContext *contextPtr = GetContextPtr(tmRecPtr);
782     TMShortCard i;
783     ActionRec *actions = NULL;
784     Boolean matchExact = False;
785     Boolean match = False;
786     StatePtr complexMatchState = NULL;
787     TMShortCard typeIndex = 0, modIndex = 0;
788     int matchTreeIndex = TM_NO_MATCH;
789 
790     LOCK_PROCESS;
791     for (i = 0;
792          ((!match || !complexMatchState) && (i < xlations->numStateTrees));
793          i++) {
794         int currIndex = -1;
795         TMSimpleStateTree stateTree =
796             (TMSimpleStateTree) xlations->stateTreeTbl[i];
797 
798         /*
799          * don't process this tree if we're only looking for a
800          * complexMatchState and there are no complex states
801          */
802         while (!(match && stateTree->isSimple) &&
803                ((!match || !complexMatchState) && (currIndex != TM_NO_MATCH))) {
804             currIndex++;
805             if (matchExact)
806                 currIndex =
807                     MatchExact(stateTree, currIndex, typeIndex, modIndex);
808             else
809                 currIndex = MatchBranchHead(stateTree, currIndex, curEventPtr);
810             if (currIndex != TM_NO_MATCH) {
811                 TMBranchHead branchHead;
812                 StatePtr currState;
813 
814                 branchHead = &stateTree->branchHeadTbl[currIndex];
815                 if (branchHead->isSimple)
816                     currState = NULL;
817                 else
818                     currState = ((TMComplexStateTree) stateTree)
819                         ->complexBranchHeadTbl[TMBranchMore(branchHead)];
820 
821                 /*
822                  * first check for a complete match
823                  */
824                 if (!match) {
825                     if (branchHead->hasActions) {
826                         if (branchHead->isSimple) {
827                             static ActionRec dummyAction;
828 
829                             dummyAction.idx = TMBranchMore(branchHead);
830                             actions = &dummyAction;
831                         }
832                         else
833                             actions = currState->actions;
834                         tmRecPtr->lastEventTime =
835                             GetTime(tmRecPtr, curEventPtr->xev);
836                         FreeContext((TMContext *) &tmRecPtr->current_state);
837                         match = True;
838                         matchTreeIndex = i;
839                     }
840                     /*
841                      * if it doesn't have actions and
842                      * it's bc mode then it's a potential match node that is
843                      * used to match later sequences.
844                      */
845                     if (!TMNewMatchSemantics() && !matchExact) {
846                         matchExact = True;
847                         typeIndex = branchHead->typeIndex;
848                         modIndex = branchHead->modIndex;
849                     }
850                 }
851                 /*
852                  * check for it being an event sequence which can be
853                  * a future match
854                  */
855                 if (!branchHead->isSimple &&
856                     !branchHead->hasActions && !complexMatchState)
857                     complexMatchState = currState;
858             }
859         }
860     }
861     if (match) {
862         TMBindData bindData = (TMBindData) tmRecPtr->proc_table;
863         XtActionProc *procs;
864         Widget accelWidget;
865 
866         if (bindData->simple.isComplex) {
867             TMComplexBindProcs bindProcs =
868                 TMGetComplexBindEntry(bindData, matchTreeIndex);
869             procs = bindProcs->procs;
870             accelWidget = bindProcs->widget;
871         }
872         else {
873             TMSimpleBindProcs bindProcs =
874                 TMGetSimpleBindEntry(bindData, matchTreeIndex);
875             procs = bindProcs->procs;
876             accelWidget = NULL;
877         }
878         HandleActions
879             (w,
880              curEventPtr->xev,
881              (TMSimpleStateTree) xlations->stateTreeTbl[matchTreeIndex],
882              accelWidget, procs, actions);
883     }
884     if (complexMatchState)
885         PushContext(contextPtr, complexMatchState);
886     UNLOCK_PROCESS;
887 }
888 
889 static int
MatchComplexBranch(TMComplexStateTree stateTree,int startIndex,TMContext context,StatePtr * leafStateRtn)890 MatchComplexBranch(TMComplexStateTree stateTree,
891                    int startIndex,
892                    TMContext context,
893                    StatePtr *leafStateRtn)
894 {
895     TMShortCard i;
896 
897     LOCK_PROCESS;
898     for (i = (TMShortCard) startIndex; i < stateTree->numComplexBranchHeads;
899          i++) {
900         StatePtr candState;
901         TMShortCard numMatches = context->numMatches;
902         MatchPair statMatch = context->matches;
903 
904         for (candState = stateTree->complexBranchHeadTbl[i];
905              numMatches && candState;
906              numMatches--, statMatch++, candState = candState->nextLevel) {
907             if ((statMatch->typeIndex != candState->typeIndex) ||
908                 (statMatch->modIndex != candState->modIndex))
909                 break;
910         }
911         if (numMatches == 0) {
912             *leafStateRtn = candState;
913             UNLOCK_PROCESS;
914             return i;
915         }
916     }
917     *leafStateRtn = NULL;
918     UNLOCK_PROCESS;
919     return (TM_NO_MATCH);
920 }
921 
922 static StatePtr
TryCurrentTree(TMComplexStateTree * stateTreePtr,XtTM tmRecPtr,TMEventRec * curEventPtr)923 TryCurrentTree(TMComplexStateTree *stateTreePtr,
924                XtTM tmRecPtr,
925                TMEventRec *curEventPtr)
926 {
927     StatePtr candState = NULL, matchState = NULL;
928     TMContext *contextPtr = GetContextPtr(tmRecPtr);
929     TMTypeMatch typeMatch;
930     TMModifierMatch modMatch;
931     int currIndex = -1;
932 
933     /*
934      * we want the first sequence that both matches and has actions.
935      * we keep on looking till we find both
936      */
937     LOCK_PROCESS;
938     while ((currIndex =
939             MatchComplexBranch(*stateTreePtr,
940                                ++currIndex, (*contextPtr), &candState))
941            != TM_NO_MATCH) {
942         if (candState != NULL) {
943             typeMatch = TMGetTypeMatch(candState->typeIndex);
944             modMatch = TMGetModifierMatch(candState->modIndex);
945 
946             /* does this state's index match? --> done */
947             if (MatchIncomingEvent(curEventPtr, typeMatch, modMatch)) {
948                 if (candState->actions) {
949                     UNLOCK_PROCESS;
950                     return candState;
951                 }
952                 else
953                     matchState = candState;
954             }
955             /* is this an event timer? */
956             if (typeMatch->eventType == _XtEventTimerEventType) {
957                 StatePtr nextState = candState->nextLevel;
958 
959                 /* does the succeeding state match? */
960                 if (nextState != NULL) {
961                     TMTypeMatch nextTypeMatch;
962                     TMModifierMatch nextModMatch;
963 
964                     nextTypeMatch = TMGetTypeMatch(nextState->typeIndex);
965                     nextModMatch = TMGetModifierMatch(nextState->modIndex);
966 
967                     /* is it within the timeout? */
968                     if (MatchIncomingEvent(curEventPtr,
969                                            nextTypeMatch, nextModMatch)) {
970                         XEvent *xev = curEventPtr->xev;
971                         unsigned long time = GetTime(tmRecPtr, xev);
972                         XtPerDisplay pd = _XtGetPerDisplay(xev->xany.display);
973                         unsigned long delta =
974                             (unsigned long) pd->multi_click_time;
975 
976                         if ((tmRecPtr->lastEventTime + delta) >= time) {
977                             if (nextState->actions) {
978                                 UNLOCK_PROCESS;
979                                 return candState;
980                             }
981                             else
982                                 matchState = candState;
983                         }
984                     }
985                 }
986             }
987         }
988     }
989     UNLOCK_PROCESS;
990     return matchState;
991 }
992 
993 static void
HandleComplexState(Widget w,XtTM tmRecPtr,TMEventRec * curEventPtr)994 HandleComplexState(Widget w, XtTM tmRecPtr, TMEventRec *curEventPtr)
995 {
996     XtTranslations xlations = tmRecPtr->translations;
997     TMContext *contextPtr = GetContextPtr(tmRecPtr);
998     TMShortCard i, matchTreeIndex = 0;
999     StatePtr matchState = NULL, candState;
1000     TMComplexStateTree *stateTreePtr =
1001         (TMComplexStateTree *) &xlations->stateTreeTbl[0];
1002 
1003     LOCK_PROCESS;
1004     for (i = 0; i < xlations->numStateTrees; i++, stateTreePtr++) {
1005         /*
1006          * some compilers sign extend Boolean bit fields so test for
1007          * false |||
1008          */
1009         if (((*stateTreePtr)->isSimple == False) &&
1010             (candState = TryCurrentTree(stateTreePtr, tmRecPtr, curEventPtr))) {
1011             if (!matchState || candState->actions) {
1012                 matchTreeIndex = i;
1013                 matchState = candState;
1014                 if (candState->actions)
1015                     break;
1016             }
1017         }
1018     }
1019     if (matchState == NULL) {
1020         /* couldn't find it... */
1021         if (!Ignore(curEventPtr)) {
1022             FreeContext(contextPtr);
1023             HandleSimpleState(w, tmRecPtr, curEventPtr);
1024         }
1025     }
1026     else {
1027         TMBindData bindData = (TMBindData) tmRecPtr->proc_table;
1028         XtActionProc *procs;
1029         Widget accelWidget;
1030         TMTypeMatch typeMatch;
1031 
1032         typeMatch = TMGetTypeMatch(matchState->typeIndex);
1033 
1034         PushContext(contextPtr, matchState);
1035         if (typeMatch->eventType == _XtEventTimerEventType) {
1036             matchState = matchState->nextLevel;
1037             PushContext(contextPtr, matchState);
1038         }
1039         tmRecPtr->lastEventTime = GetTime(tmRecPtr, curEventPtr->xev);
1040 
1041         if (bindData->simple.isComplex) {
1042             TMComplexBindProcs bindProcs =
1043                 TMGetComplexBindEntry(bindData, matchTreeIndex);
1044             procs = bindProcs->procs;
1045             accelWidget = bindProcs->widget;
1046         }
1047         else {
1048             TMSimpleBindProcs bindProcs =
1049                 TMGetSimpleBindEntry(bindData, matchTreeIndex);
1050             procs = bindProcs->procs;
1051             accelWidget = NULL;
1052         }
1053         HandleActions(w, curEventPtr->xev, (TMSimpleStateTree)
1054                       xlations->stateTreeTbl[matchTreeIndex],
1055                       accelWidget, procs, matchState->actions);
1056     }
1057     UNLOCK_PROCESS;
1058 }
1059 
1060 void
_XtTranslateEvent(Widget w,XEvent * event)1061 _XtTranslateEvent(Widget w, XEvent *event)
1062 {
1063     XtTM tmRecPtr = &w->core.tm;
1064     TMEventRec curEvent;
1065     StatePtr current_state = tmRecPtr->current_state;
1066 
1067     XEventToTMEvent(event, &curEvent);
1068 
1069     if (!tmRecPtr->translations) {
1070         XtAppWarningMsg(XtWidgetToApplicationContext(w),
1071                         XtNtranslationError, "nullTable", XtCXtToolkitError,
1072                         "Can't translate event through NULL table", NULL, NULL);
1073         return;
1074     }
1075     if (current_state == NULL)
1076         HandleSimpleState(w, tmRecPtr, &curEvent);
1077     else
1078         HandleComplexState(w, tmRecPtr, &curEvent);
1079 }
1080 
1081 static StatePtr
NewState(TMParseStateTree stateTree _X_UNUSED,TMShortCard typeIndex,TMShortCard modIndex)1082 NewState(TMParseStateTree stateTree _X_UNUSED,
1083          TMShortCard typeIndex,
1084          TMShortCard modIndex)
1085 {
1086     StatePtr state = XtNew(StateRec);
1087 
1088 #ifdef TRACE_TM
1089     LOCK_PROCESS;
1090     _XtGlobalTM.numComplexStates++;
1091     UNLOCK_PROCESS;
1092 #endif                          /* TRACE_TM */
1093     state->typeIndex = typeIndex;
1094     state->modIndex = modIndex;
1095     state->nextLevel = NULL;
1096     state->actions = NULL;
1097     state->isCycleStart = state->isCycleEnd = False;
1098     return state;
1099 }
1100 
1101 /*
1102  * This routine is an iterator for state trees. If the func returns
1103  * true then iteration is over.
1104  */
1105 void
_XtTraverseStateTree(TMStateTree tree,_XtTraversalProc func,XtPointer data)1106 _XtTraverseStateTree(TMStateTree tree, _XtTraversalProc func, XtPointer data)
1107 {
1108     TMComplexStateTree stateTree = (TMComplexStateTree) tree;
1109     TMBranchHead currBH;
1110     TMShortCard i;
1111     StateRec dummyStateRec, *dummyState = &dummyStateRec;
1112     ActionRec dummyActionRec, *dummyAction = &dummyActionRec;
1113     Boolean firstSimple = True;
1114     StatePtr currState;
1115 
1116     /* first traverse the complex states */
1117     if (stateTree->isSimple == False)
1118         for (i = 0; i < stateTree->numComplexBranchHeads; i++) {
1119             currState = stateTree->complexBranchHeadTbl[i];
1120             for (; currState; currState = currState->nextLevel) {
1121                 if (func(currState, data))
1122                     return;
1123                 if (currState->isCycleEnd)
1124                     break;
1125             }
1126         }
1127 
1128     /* now traverse the simple ones */
1129     for (i = 0, currBH = stateTree->branchHeadTbl;
1130          i < stateTree->numBranchHeads; i++, currBH++) {
1131         if (currBH->isSimple && currBH->hasActions) {
1132             if (firstSimple) {
1133                 XtBZero((char *) dummyState, sizeof(StateRec));
1134                 XtBZero((char *) dummyAction, sizeof(ActionRec));
1135                 dummyState->actions = dummyAction;
1136                 firstSimple = False;
1137             }
1138             dummyState->typeIndex = currBH->typeIndex;
1139             dummyState->modIndex = currBH->modIndex;
1140             dummyAction->idx = currBH->more;
1141             if (func(dummyState, data))
1142                 return;
1143         }
1144     }
1145 }
1146 
1147 static EventMask
EventToMask(TMTypeMatch typeMatch,TMModifierMatch modMatch)1148 EventToMask(TMTypeMatch typeMatch, TMModifierMatch modMatch)
1149 {
1150     EventMask returnMask;
1151     unsigned long eventType = typeMatch->eventType;
1152 
1153     if (eventType == MotionNotify) {
1154         Modifiers modifierMask = (Modifiers) modMatch->modifierMask;
1155         Modifiers tempMask;
1156 
1157         returnMask = 0;
1158         if (modifierMask == 0) {
1159             if (modMatch->modifiers == AnyButtonMask)
1160                 return ButtonMotionMask;
1161             else
1162                 return PointerMotionMask;
1163         }
1164         tempMask = modifierMask &
1165             (Button1Mask | Button2Mask | Button3Mask
1166              | Button4Mask | Button5Mask);
1167         if (tempMask == 0)
1168             return PointerMotionMask;
1169         if (tempMask & Button1Mask)
1170             returnMask |= Button1MotionMask;
1171         if (tempMask & Button2Mask)
1172             returnMask |= Button2MotionMask;
1173         if (tempMask & Button3Mask)
1174             returnMask |= Button3MotionMask;
1175         if (tempMask & Button4Mask)
1176             returnMask |= Button4MotionMask;
1177         if (tempMask & Button5Mask)
1178             returnMask |= Button5MotionMask;
1179         return returnMask;
1180     }
1181     returnMask = _XtConvertTypeToMask((int) eventType);
1182     if (returnMask == (StructureNotifyMask | SubstructureNotifyMask))
1183         returnMask = StructureNotifyMask;
1184     return returnMask;
1185 }
1186 
1187 static void
DispatchMappingNotify(Widget widget _X_UNUSED,XtPointer closure,XtPointer call_data)1188 DispatchMappingNotify(Widget widget _X_UNUSED,  /* will be NULL from _RefreshMapping */
1189                       XtPointer closure,        /* real Widget */
1190                       XtPointer call_data)      /* XEvent* */
1191 {
1192     _XtTranslateEvent((Widget) closure, (XEvent *) call_data);
1193 }
1194 
1195 static void
RemoveFromMappingCallbacks(Widget widget,XtPointer closure,XtPointer call_data _X_UNUSED)1196 RemoveFromMappingCallbacks(Widget widget,
1197                            XtPointer closure,    /* target widget */
1198                            XtPointer call_data _X_UNUSED)
1199 {
1200     _XtRemoveCallback(&_XtGetPerDisplay(XtDisplay(widget))->mapping_callbacks,
1201                       DispatchMappingNotify, closure);
1202 }
1203 
1204 static Boolean
AggregateEventMask(StatePtr state,XtPointer data)1205 AggregateEventMask(StatePtr state, XtPointer data)
1206 {
1207     LOCK_PROCESS;
1208     *((EventMask *) data) |= EventToMask(TMGetTypeMatch(state->typeIndex),
1209                                          TMGetModifierMatch(state->modIndex));
1210     UNLOCK_PROCESS;
1211     return False;
1212 }
1213 
1214 void
_XtInstallTranslations(Widget widget)1215 _XtInstallTranslations(Widget widget)
1216 {
1217     XtTranslations xlations;
1218     Cardinal i;
1219     Boolean mappingNotifyInterest = False;
1220 
1221     xlations = widget->core.tm.translations;
1222     if (xlations == NULL)
1223         return;
1224 
1225     /*
1226      * check for somebody stuffing the translations directly into the
1227      * instance structure. We will end up being called again out of
1228      * ComposeTranslations but we *should* have bindings by then
1229      */
1230     if (widget->core.tm.proc_table == NULL) {
1231         _XtMergeTranslations(widget, NULL, XtTableReplace);
1232         /*
1233          * if we're realized then we'll be called out of
1234          * ComposeTranslations
1235          */
1236         if (XtIsRealized(widget))
1237             return;
1238     }
1239 
1240     xlations->eventMask = 0;
1241     for (i = 0; i < xlations->numStateTrees; i++) {
1242         TMStateTree stateTree = xlations->stateTreeTbl[i];
1243 
1244         _XtTraverseStateTree(stateTree,
1245                              AggregateEventMask,
1246                              (XtPointer) &xlations->eventMask);
1247         mappingNotifyInterest =
1248             (Boolean) (mappingNotifyInterest |
1249                        stateTree->simple.mappingNotifyInterest);
1250     }
1251     /* double click needs to make sure that you have selected on both
1252        button down and up. */
1253 
1254     if (xlations->eventMask & ButtonPressMask)
1255         xlations->eventMask |= ButtonReleaseMask;
1256     if (xlations->eventMask & ButtonReleaseMask)
1257         xlations->eventMask |= ButtonPressMask;
1258 
1259     if (mappingNotifyInterest) {
1260         XtPerDisplay pd = _XtGetPerDisplay(XtDisplay(widget));
1261 
1262         if (pd->mapping_callbacks)
1263             _XtAddCallbackOnce(&(pd->mapping_callbacks),
1264                                DispatchMappingNotify, (XtPointer) widget);
1265         else
1266             _XtAddCallback(&(pd->mapping_callbacks),
1267                            DispatchMappingNotify, (XtPointer) widget);
1268 
1269         if (widget->core.destroy_callbacks != NULL)
1270             _XtAddCallbackOnce((InternalCallbackList *)
1271                                &widget->core.destroy_callbacks,
1272                                RemoveFromMappingCallbacks, (XtPointer) widget);
1273         else
1274             _XtAddCallback((InternalCallbackList *)
1275                            &widget->core.destroy_callbacks,
1276                            RemoveFromMappingCallbacks, (XtPointer) widget);
1277     }
1278     _XtBindActions(widget, (XtTM) &widget->core.tm);
1279     _XtRegisterGrabs(widget);
1280 }
1281 
1282 void
_XtRemoveTranslations(Widget widget)1283 _XtRemoveTranslations(Widget widget)
1284 {
1285     Cardinal i;
1286     Boolean mappingNotifyInterest = False;
1287     XtTranslations xlations = widget->core.tm.translations;
1288 
1289     if (xlations == NULL)
1290         return;
1291 
1292     for (i = 0; i < xlations->numStateTrees; i++) {
1293         TMSimpleStateTree stateTree =
1294             (TMSimpleStateTree) xlations->stateTreeTbl[i];
1295         mappingNotifyInterest =
1296             (Boolean) (mappingNotifyInterest |
1297                        stateTree->mappingNotifyInterest);
1298     }
1299     if (mappingNotifyInterest)
1300         RemoveFromMappingCallbacks(widget, (XtPointer) widget, NULL);
1301 }
1302 
1303 static void
_XtUninstallTranslations(Widget widget)1304 _XtUninstallTranslations(Widget widget)
1305 {
1306     XtTranslations xlations = widget->core.tm.translations;
1307 
1308     _XtUnbindActions(widget, xlations, (TMBindData) widget->core.tm.proc_table);
1309     _XtRemoveTranslations(widget);
1310     widget->core.tm.translations = NULL;
1311     FreeContext((TMContext *) &widget->core.tm.current_state);
1312 }
1313 
1314 void
_XtDestroyTMData(Widget widget)1315 _XtDestroyTMData(Widget widget)
1316 {
1317     TMComplexBindData cBindData;
1318 
1319     _XtUninstallTranslations(widget);
1320 
1321     if ((cBindData = (TMComplexBindData) widget->core.tm.proc_table)) {
1322         if (cBindData->isComplex) {
1323             ATranslations nXlations = (ATranslations) cBindData->accel_context;
1324 
1325             while (nXlations) {
1326                 ATranslations aXlations = nXlations;
1327 
1328                 nXlations = nXlations->next;
1329                 XtFree((char *) aXlations);
1330             }
1331         }
1332         XtFree((char *) cBindData);
1333     }
1334 }
1335 
1336 /*** Public procedures ***/
1337 
1338 void
XtUninstallTranslations(Widget widget)1339 XtUninstallTranslations(Widget widget)
1340 {
1341     EventMask oldMask;
1342     Widget hookobj;
1343 
1344     WIDGET_TO_APPCON(widget);
1345 
1346     LOCK_APP(app);
1347     if (!widget->core.tm.translations) {
1348         UNLOCK_APP(app);
1349         return;
1350     }
1351     oldMask = widget->core.tm.translations->eventMask;
1352     _XtUninstallTranslations(widget);
1353     if (XtIsRealized(widget) && oldMask)
1354         XSelectInput(XtDisplay(widget), XtWindow(widget),
1355                      (long) XtBuildEventMask(widget));
1356     hookobj = XtHooksOfDisplay(XtDisplayOfObject(widget));
1357     if (XtHasCallbacks(hookobj, XtNchangeHook) == XtCallbackHasSome) {
1358         XtChangeHookDataRec call_data;
1359 
1360         call_data.type = XtHuninstallTranslations;
1361         call_data.widget = widget;
1362         XtCallCallbackList(hookobj,
1363                            ((HookObject) hookobj)->hooks.changehook_callbacks,
1364                            (XtPointer) &call_data);
1365     }
1366     UNLOCK_APP(app);
1367 }
1368 
1369 XtTranslations
_XtCreateXlations(TMStateTree * stateTrees,TMShortCard numStateTrees,XtTranslations first,XtTranslations second)1370 _XtCreateXlations(TMStateTree *stateTrees,
1371                   TMShortCard numStateTrees,
1372                   XtTranslations first,
1373                   XtTranslations second)
1374 {
1375     XtTranslations xlations;
1376     TMShortCard i;
1377 
1378     xlations = (XtTranslations)
1379         __XtMalloc((Cardinal) (sizeof(TranslationData) +
1380                                (size_t) (numStateTrees -
1381                                          1) * sizeof(TMStateTree)));
1382 #ifdef TRACE_TM
1383     LOCK_PROCESS;
1384     if (_XtGlobalTM.numTms == _XtGlobalTM.tmTblSize) {
1385         _XtGlobalTM.tmTblSize = (TMShortCard) (_XtGlobalTM.tmTblSize + 16);
1386         _XtGlobalTM.tmTbl = (XtTranslations *)
1387             XtRealloc((char *) _XtGlobalTM.tmTbl,
1388                       (Cardinal) (_XtGlobalTM.tmTblSize *
1389                                   sizeof(XtTranslations)));
1390     }
1391     _XtGlobalTM.tmTbl[_XtGlobalTM.numTms++] = xlations;
1392     UNLOCK_PROCESS;
1393 #endif                          /* TRACE_TM */
1394 
1395     xlations->composers[0] = first;
1396     xlations->composers[1] = second;
1397     xlations->hasBindings = False;
1398     xlations->operation = XtTableReplace;
1399 
1400     for (i = 0; i < numStateTrees; i++) {
1401         xlations->stateTreeTbl[i] = (TMStateTree) stateTrees[i];
1402         stateTrees[i]->simple.refCount++;
1403     }
1404     xlations->numStateTrees = numStateTrees;
1405     xlations->eventMask = 0;
1406     return xlations;
1407 }
1408 
1409 TMStateTree
_XtParseTreeToStateTree(TMParseStateTree parseTree)1410 _XtParseTreeToStateTree(TMParseStateTree parseTree)
1411 {
1412     TMSimpleStateTree simpleTree;
1413     unsigned int tableSize;
1414 
1415     if (parseTree->numComplexBranchHeads) {
1416         TMComplexStateTree complexTree;
1417 
1418         complexTree = XtNew(TMComplexStateTreeRec);
1419         complexTree->isSimple = False;
1420         tableSize =
1421             (unsigned) (parseTree->numComplexBranchHeads * sizeof(StatePtr));
1422         complexTree->complexBranchHeadTbl = (StatePtr *)
1423             __XtMalloc(tableSize);
1424         XtMemmove(complexTree->complexBranchHeadTbl,
1425                   parseTree->complexBranchHeadTbl, tableSize);
1426         complexTree->numComplexBranchHeads = parseTree->numComplexBranchHeads;
1427         simpleTree = (TMSimpleStateTree) complexTree;
1428     }
1429     else {
1430         simpleTree = XtNew(TMSimpleStateTreeRec);
1431         simpleTree->isSimple = True;
1432     }
1433     simpleTree->isAccelerator = parseTree->isAccelerator;
1434     simpleTree->refCount = 0;
1435     simpleTree->mappingNotifyInterest = parseTree->mappingNotifyInterest;
1436 
1437     tableSize =
1438         (unsigned) (parseTree->numBranchHeads * sizeof(TMBranchHeadRec));
1439     simpleTree->branchHeadTbl = (TMBranchHead)
1440         __XtMalloc(tableSize);
1441     XtMemmove(simpleTree->branchHeadTbl, parseTree->branchHeadTbl, tableSize);
1442     simpleTree->numBranchHeads = parseTree->numBranchHeads;
1443 
1444     tableSize = (unsigned) (parseTree->numQuarks * sizeof(XrmQuark));
1445     simpleTree->quarkTbl = (XrmQuark *) __XtMalloc(tableSize);
1446     XtMemmove(simpleTree->quarkTbl, parseTree->quarkTbl, tableSize);
1447     simpleTree->numQuarks = parseTree->numQuarks;
1448 
1449     return (TMStateTree) simpleTree;
1450 }
1451 
1452 static void
FreeActions(ActionPtr actions)1453 FreeActions(ActionPtr actions)
1454 {
1455     ActionPtr action;
1456     TMShortCard i;
1457 
1458     for (action = actions; action;) {
1459         ActionPtr nextAction = action->next;
1460 
1461         for (i = (TMShortCard) action->num_params; i;) {
1462             XtFree((_XtString) action->params[--i]);
1463         }
1464         XtFree((char *) action->params);
1465         XtFree((char *) action);
1466         action = nextAction;
1467     }
1468 }
1469 
1470 static void
AmbigActions(EventSeqPtr initialEvent,StatePtr * state,TMParseStateTree stateTree)1471 AmbigActions(EventSeqPtr initialEvent,
1472              StatePtr *state,
1473              TMParseStateTree stateTree)
1474 {
1475     String params[3];
1476     Cardinal numParams = 0;
1477 
1478     params[numParams++] = _XtPrintEventSeq(initialEvent, NULL);
1479     params[numParams++] = _XtPrintActions((*state)->actions,
1480                                           stateTree->quarkTbl);
1481     XtWarningMsg(XtNtranslationError, "oldActions", XtCXtToolkitError,
1482                  "Previous entry was: %s %s", params, &numParams);
1483     XtFree((char *) params[0]);
1484     XtFree((char *) params[1]);
1485     numParams = 0;
1486     params[numParams++] = _XtPrintActions(initialEvent->actions,
1487                                           stateTree->quarkTbl);
1488     XtWarningMsg(XtNtranslationError, "newActions", XtCXtToolkitError,
1489                  "New actions are:%s", params, &numParams);
1490     XtFree((char *) params[0]);
1491     XtWarningMsg(XtNtranslationError, "ambiguousActions",
1492                  XtCXtToolkitError,
1493                  "Overriding earlier translation manager actions.", NULL, NULL);
1494 
1495     FreeActions((*state)->actions);
1496     (*state)->actions = NULL;
1497 }
1498 
1499 void
_XtAddEventSeqToStateTree(EventSeqPtr eventSeq,TMParseStateTree stateTree)1500 _XtAddEventSeqToStateTree(EventSeqPtr eventSeq, TMParseStateTree stateTree)
1501 {
1502     StatePtr *state;
1503     EventSeqPtr initialEvent = eventSeq;
1504     TMBranchHead branchHead;
1505     TMShortCard idx, modIndex, typeIndex;
1506 
1507     if (eventSeq == NULL)
1508         return;
1509 
1510     /* note that all states in the event seq passed in start out null */
1511     /* we fill them in with the matching state as we traverse the list */
1512 
1513     /*
1514      * We need to free the parser data structures !!!
1515      */
1516 
1517     typeIndex = _XtGetTypeIndex(&eventSeq->event);
1518     modIndex = _XtGetModifierIndex(&eventSeq->event);
1519     idx = GetBranchHead(stateTree, typeIndex, modIndex, False);
1520     branchHead = &stateTree->branchHeadTbl[idx];
1521 
1522     /*
1523      * Need to check for pre-existing actions with same lhs |||
1524      */
1525 
1526     /*
1527      * Check for optimized case. Don't assume that the eventSeq has actions.
1528      */
1529     if (!eventSeq->next &&
1530         eventSeq->actions &&
1531         !eventSeq->actions->next && !eventSeq->actions->num_params) {
1532         if (eventSeq->event.eventType == MappingNotify)
1533             stateTree->mappingNotifyInterest = True;
1534         branchHead->hasActions = True;
1535         XtSetBits(branchHead->more, eventSeq->actions->idx, 13);
1536         FreeActions(eventSeq->actions);
1537         eventSeq->actions = NULL;
1538         return;
1539     }
1540 
1541     branchHead->isSimple = False;
1542     if (!eventSeq->next)
1543         branchHead->hasActions = True;
1544     XtSetBits(branchHead->more,
1545               GetComplexBranchIndex(stateTree, typeIndex, modIndex), 13);
1546     state = &stateTree->complexBranchHeadTbl[TMBranchMore(branchHead)];
1547 
1548     for (;;) {
1549         *state = NewState(stateTree, typeIndex, modIndex);
1550 
1551         if (eventSeq->event.eventType == MappingNotify)
1552             stateTree->mappingNotifyInterest = True;
1553 
1554         /* *state now points at state record matching event */
1555         eventSeq->state = *state;
1556 
1557         if (eventSeq->actions != NULL) {
1558             if ((*state)->actions != NULL)
1559                 AmbigActions(initialEvent, state, stateTree);
1560             (*state)->actions = eventSeq->actions;
1561 #ifdef TRACE_TM
1562             LOCK_PROCESS;
1563             _XtGlobalTM.numComplexActions++;
1564             UNLOCK_PROCESS;
1565 #endif                          /* TRACE_TM */
1566         }
1567 
1568         if (((eventSeq = eventSeq->next) == NULL) || (eventSeq->state))
1569             break;
1570 
1571         state = &(*state)->nextLevel;
1572         typeIndex = _XtGetTypeIndex(&eventSeq->event);
1573         modIndex = _XtGetModifierIndex(&eventSeq->event);
1574         LOCK_PROCESS;
1575         if (!TMNewMatchSemantics()) {
1576             /*
1577              * force a potential empty entry into the branch head
1578              * table in order to emulate old matching behavior
1579              */
1580             (void) GetBranchHead(stateTree, typeIndex, modIndex, True);
1581         }
1582         UNLOCK_PROCESS;
1583     }
1584 
1585     if (eventSeq && eventSeq->state) {
1586         /* we've been here before... must be a cycle in the event seq. */
1587         branchHead->hasCycles = True;
1588         (*state)->nextLevel = eventSeq->state;
1589         eventSeq->state->isCycleStart = True;
1590         (*state)->isCycleEnd = TRUE;
1591     }
1592 }
1593 
1594 /*
1595  * Internal Converter for merging. Old and New must both be valid xlations
1596  */
1597 Boolean
_XtCvtMergeTranslations(Display * dpy _X_UNUSED,XrmValuePtr args _X_UNUSED,Cardinal * num_args,XrmValuePtr from,XrmValuePtr to,XtPointer * closure_ret _X_UNUSED)1598 _XtCvtMergeTranslations(Display *dpy _X_UNUSED,
1599                         XrmValuePtr args _X_UNUSED,
1600                         Cardinal *num_args,
1601                         XrmValuePtr from,
1602                         XrmValuePtr to,
1603                         XtPointer *closure_ret _X_UNUSED)
1604 {
1605     XtTranslations first, second, xlations;
1606     TMStateTree *stateTrees, stackStateTrees[16];
1607     TMShortCard numStateTrees, i;
1608 
1609     if (*num_args != 0)
1610         XtWarningMsg("invalidParameters", "mergeTranslations",
1611                      XtCXtToolkitError,
1612                      "MergeTM to TranslationTable needs no extra arguments",
1613                      NULL, NULL);
1614 
1615     if (to->addr != NULL && to->size < sizeof(XtTranslations)) {
1616         to->size = sizeof(XtTranslations);
1617         return False;
1618     }
1619 
1620     first = ((TMConvertRec *) from->addr)->old;
1621     second = ((TMConvertRec *) from->addr)->new;
1622 
1623     numStateTrees =
1624         (TMShortCard) (first->numStateTrees + second->numStateTrees);
1625 
1626     stateTrees = (TMStateTree *)
1627         XtStackAlloc(numStateTrees * sizeof(TMStateTree), stackStateTrees);
1628 
1629     for (i = 0; i < first->numStateTrees; i++)
1630         stateTrees[i] = first->stateTreeTbl[i];
1631     for (i = 0; i < second->numStateTrees; i++)
1632         stateTrees[i + first->numStateTrees] = second->stateTreeTbl[i];
1633 
1634     xlations = _XtCreateXlations(stateTrees, numStateTrees, first, second);
1635 
1636     if (to->addr != NULL) {
1637         *(XtTranslations *) to->addr = xlations;
1638     }
1639     else {
1640         static XtTranslations staticStateTable;
1641 
1642         staticStateTable = xlations;
1643         to->addr = (XPointer) &staticStateTable;
1644         to->size = sizeof(XtTranslations);
1645     }
1646 
1647     XtStackFree((XtPointer) stateTrees, (XtPointer) stackStateTrees);
1648     return True;
1649 }
1650 
1651 static XtTranslations
MergeThem(Widget dest,XtTranslations first,XtTranslations second)1652 MergeThem(Widget dest, XtTranslations first, XtTranslations second)
1653 {
1654     XtCacheRef cache_ref;
1655     static XrmQuark from_type = NULLQUARK, to_type;
1656     XrmValue from, to;
1657     TMConvertRec convert_rec;
1658     XtTranslations newTable;
1659 
1660     LOCK_PROCESS;
1661     if (from_type == NULLQUARK) {
1662         from_type = XrmPermStringToQuark(_XtRStateTablePair);
1663         to_type = XrmPermStringToQuark(XtRTranslationTable);
1664     }
1665     UNLOCK_PROCESS;
1666     from.addr = (XPointer) &convert_rec;
1667     from.size = sizeof(TMConvertRec);
1668     to.addr = (XPointer) &newTable;
1669     to.size = sizeof(XtTranslations);
1670     convert_rec.old = first;
1671     convert_rec.new = second;
1672 
1673     LOCK_PROCESS;
1674     if (!_XtConvert(dest, from_type, &from, to_type, &to, &cache_ref)) {
1675         UNLOCK_PROCESS;
1676         return NULL;
1677     }
1678     UNLOCK_PROCESS;
1679 
1680 #ifndef REFCNT_TRANSLATIONS
1681 
1682     if (cache_ref)
1683         XtAddCallback(dest, XtNdestroyCallback,
1684                       XtCallbackReleaseCacheRef, (XtPointer) cache_ref);
1685 
1686 #endif
1687 
1688     return newTable;
1689 }
1690 
1691 /*
1692  * Unmerge will recursively traverse the xlation compose tree and
1693  * generate a new xlation that is the result of all instances of
1694  * xlations being removed. It currently doesn't differentiate between
1695  * the potential that an xlation will be both an accelerator and
1696  * normal. This is not supported by the spec anyway.
1697  */
1698 static XtTranslations
UnmergeTranslations(Widget widget,XtTranslations xlations,XtTranslations unmergeXlations,TMShortCard currIndex,TMComplexBindProcs oldBindings,TMShortCard numOldBindings,TMComplexBindProcs newBindings,TMShortCard * numNewBindingsRtn)1699 UnmergeTranslations(Widget widget,
1700                     XtTranslations xlations,
1701                     XtTranslations unmergeXlations,
1702                     TMShortCard currIndex,
1703                     TMComplexBindProcs oldBindings,
1704                     TMShortCard numOldBindings,
1705                     TMComplexBindProcs newBindings,
1706                     TMShortCard *numNewBindingsRtn)
1707 {
1708     XtTranslations first, second, result;
1709 
1710     if (!xlations || (xlations == unmergeXlations))
1711         return NULL;
1712 
1713     if (xlations->composers[0]) {
1714         first = UnmergeTranslations(widget, xlations->composers[0],
1715                                     unmergeXlations, currIndex,
1716                                     oldBindings, numOldBindings,
1717                                     newBindings, numNewBindingsRtn);
1718     }
1719     else
1720         first = NULL;
1721 
1722     if (xlations->composers[0]
1723         && xlations->composers[1]) {
1724         second = UnmergeTranslations(widget, xlations->composers[1],
1725                                      unmergeXlations, (TMShortCard)
1726                                      (currIndex +
1727                                       xlations->composers[0]->numStateTrees),
1728                                      oldBindings,
1729                                      numOldBindings, newBindings,
1730                                      numNewBindingsRtn);
1731     }
1732     else
1733         second = NULL;
1734 
1735     if (first || second) {
1736         if (first && second) {
1737             if ((first != xlations->composers[0]) ||
1738                 (second != xlations->composers[1]))
1739                 result = MergeThem(widget, first, second);
1740             else
1741                 result = xlations;
1742         }
1743         else {
1744             if (first)
1745                 result = first;
1746             else
1747                 result = second;
1748         }
1749     }
1750     else {                      /* only update for leaf nodes */
1751         if (numOldBindings) {
1752             Cardinal i;
1753 
1754             for (i = 0; i < xlations->numStateTrees; i++) {
1755                 if (xlations->stateTreeTbl[i]->simple.isAccelerator)
1756                     newBindings[*numNewBindingsRtn] =
1757                         oldBindings[currIndex + i];
1758                 (*numNewBindingsRtn)++;
1759             }
1760         }
1761         result = xlations;
1762     }
1763     return result;
1764 }
1765 
1766 typedef struct {
1767     XtTranslations xlations;
1768     TMComplexBindProcs bindings;
1769 } MergeBindRec, *MergeBind;
1770 
1771 static XtTranslations
MergeTranslations(Widget widget,XtTranslations oldXlations,XtTranslations newXlations,_XtTranslateOp operation,Widget source,TMComplexBindProcs oldBindings,TMComplexBindProcs newBindings,TMShortCard * numNewRtn)1772 MergeTranslations(Widget widget,
1773                   XtTranslations oldXlations,
1774                   XtTranslations newXlations,
1775                   _XtTranslateOp operation,
1776                   Widget source,
1777                   TMComplexBindProcs oldBindings,
1778                   TMComplexBindProcs newBindings,
1779                   TMShortCard *numNewRtn)
1780 {
1781     XtTranslations newTable = NULL, xlations;
1782     TMComplexBindProcs bindings;
1783     TMShortCard i, j;
1784     TMStateTree *treePtr;
1785     TMShortCard numNew;
1786     MergeBindRec bindPair[2];
1787 
1788     /* If the new translation has an accelerator context then pull it
1789      * off and pass it and the real xlations in to the caching merge
1790      * routine.
1791      */
1792     if (newXlations->hasBindings) {
1793         xlations = ((ATranslations) newXlations)->xlations;
1794         bindings = (TMComplexBindProcs)
1795             &((ATranslations) newXlations)->bindTbl[0];
1796     }
1797     else {
1798         xlations = newXlations;
1799         bindings = NULL;
1800     }
1801     switch (operation) {
1802     case XtTableReplace:
1803         newTable = bindPair[0].xlations = xlations;
1804         bindPair[0].bindings = bindings;
1805         bindPair[1].xlations = NULL;
1806         bindPair[1].bindings = NULL;
1807         break;
1808     case XtTableAugment:
1809         bindPair[0].xlations = oldXlations;
1810         bindPair[0].bindings = oldBindings;
1811         bindPair[1].xlations = xlations;
1812         bindPair[1].bindings = bindings;
1813         newTable = NULL;
1814         break;
1815     case XtTableOverride:
1816         bindPair[0].xlations = xlations;
1817         bindPair[0].bindings = bindings;
1818         bindPair[1].xlations = oldXlations;
1819         bindPair[1].bindings = oldBindings;
1820         newTable = NULL;
1821         break;
1822     }
1823     if (!newTable)
1824         newTable =
1825             MergeThem(widget, bindPair[0].xlations, bindPair[1].xlations);
1826 
1827     for (i = 0, numNew = 0; i < 2; i++) {
1828         if (bindPair[i].xlations)
1829             for (j = 0; j < bindPair[i].xlations->numStateTrees; j++, numNew++) {
1830                 if (bindPair[i].xlations->stateTreeTbl[j]->simple.isAccelerator) {
1831                     if (bindPair[i].bindings)
1832                         newBindings[numNew] = bindPair[i].bindings[j];
1833                     else {
1834                         newBindings[numNew].widget = source;
1835                         newBindings[numNew].aXlations = bindPair[i].xlations;
1836                     }
1837                 }
1838             }
1839     }
1840     *numNewRtn = numNew;
1841     treePtr = &newTable->stateTreeTbl[0];
1842     for (i = 0; i < newTable->numStateTrees; i++, treePtr++)
1843         (*treePtr)->simple.refCount++;
1844     return newTable;
1845 }
1846 
1847 static TMBindData
MakeBindData(TMComplexBindProcs bindings,TMShortCard numBindings,TMBindData oldBindData)1848 MakeBindData(TMComplexBindProcs bindings,
1849              TMShortCard numBindings,
1850              TMBindData oldBindData)
1851 {
1852     TMLongCard bytes;
1853     TMShortCard i;
1854     Boolean isComplex;
1855     TMBindData bindData;
1856 
1857     if (numBindings == 0)
1858         return NULL;
1859     for (i = 0; i < numBindings; i++)
1860         if (bindings[i].widget)
1861             break;
1862     isComplex = (i < numBindings);
1863     if (isComplex)
1864         bytes = (sizeof(TMComplexBindDataRec) +
1865                  ((TMLongCard) (numBindings - 1) *
1866                   sizeof(TMComplexBindProcsRec)));
1867     else
1868         bytes = (sizeof(TMSimpleBindDataRec) +
1869                  ((TMLongCard) (numBindings - 1) *
1870                   sizeof(TMSimpleBindProcsRec)));
1871 
1872     bindData =
1873         (TMBindData) __XtCalloc((Cardinal) sizeof(char), (Cardinal) bytes);
1874     XtSetBit(bindData->simple.isComplex, isComplex);
1875     if (isComplex) {
1876         TMComplexBindData cBindData = (TMComplexBindData) bindData;
1877 
1878         /*
1879          * If there were any accelerator contexts in the old bindData
1880          * then propagate them to the new one.
1881          */
1882         if (oldBindData && oldBindData->simple.isComplex)
1883             cBindData->accel_context =
1884                 ((TMComplexBindData) oldBindData)->accel_context;
1885         XtMemmove((char *) &cBindData->bindTbl[0], (char *) bindings,
1886                   numBindings * sizeof(TMComplexBindProcsRec));
1887     }
1888     return bindData;
1889 }
1890 
1891 /*
1892  * This routine is the central clearinghouse for merging translations
1893  * into a widget. It takes care of preping the action bindings for
1894  * realize time and calling the converter or doing a straight merge if
1895  * the destination is empty.
1896  */
1897 static Boolean
ComposeTranslations(Widget dest,_XtTranslateOp operation,Widget source,XtTranslations newXlations)1898 ComposeTranslations(Widget dest,
1899                     _XtTranslateOp operation,
1900                     Widget source,
1901                     XtTranslations newXlations)
1902 {
1903     XtTranslations newTable, oldXlations;
1904     XtTranslations accNewXlations;
1905     EventMask oldMask = 0;
1906     TMBindData bindData;
1907     TMComplexBindProcs oldBindings = NULL;
1908     TMShortCard numOldBindings = 0, numNewBindings = 0, numBytes;
1909     TMComplexBindProcsRec stackBindings[16], *newBindings;
1910 
1911     /*
1912      * how should we be handling the refcount decrement for the
1913      * replaced translation table ???
1914      */
1915     if (!newXlations) {
1916         XtAppWarningMsg(XtWidgetToApplicationContext(dest),
1917                         XtNtranslationError, "nullTable", XtCXtToolkitError,
1918                         "table to (un)merge must not be null", NULL, NULL);
1919         return False;
1920     }
1921 
1922     accNewXlations = newXlations;
1923     newXlations = ((newXlations->hasBindings)
1924                    ? ((ATranslations) newXlations)->xlations : newXlations);
1925 
1926     if (!(oldXlations = dest->core.tm.translations))
1927         operation = XtTableReplace;
1928 
1929     /*
1930      * try to avoid generation of duplicate state trees. If the source
1931      * isn't simple (1 state Tree) then it's too much hassle
1932      */
1933     if (((operation == XtTableAugment) ||
1934          (operation == XtTableOverride)) && (newXlations->numStateTrees == 1)) {
1935         Cardinal i;
1936 
1937         for (i = 0; i < oldXlations->numStateTrees; i++)
1938             if (oldXlations->stateTreeTbl[i] == newXlations->stateTreeTbl[0])
1939                 break;
1940         if (i < oldXlations->numStateTrees) {
1941             if (operation == XtTableAugment) {
1942                 /*
1943                  * we don't need to do anything since it's already
1944                  * there
1945                  */
1946                 return True;
1947             }
1948             else {              /* operation == XtTableOverride */
1949                 /*
1950                  * We'll get rid of the duplicate trees throughout the
1951                  * and leave it with a pruned translation table. This
1952                  * will only work if the same table has been merged
1953                  * into this table (or one of it's composers
1954                  */
1955                 _XtUnmergeTranslations(dest, newXlations);
1956                 /*
1957                  * reset oldXlations so we're back in sync
1958                  */
1959                 if (!(oldXlations = dest->core.tm.translations))
1960                     operation = XtTableReplace;
1961             }
1962         }
1963     }
1964 
1965     bindData = (TMBindData) dest->core.tm.proc_table;
1966     if (bindData) {
1967         numOldBindings = (oldXlations ? oldXlations->numStateTrees : 0);
1968         if (bindData->simple.isComplex)
1969             oldBindings = &((TMComplexBindData) bindData)->bindTbl[0];
1970         else
1971             oldBindings = (TMComplexBindProcs)
1972                 (&((TMSimpleBindData) bindData)->bindTbl[0]);
1973     }
1974 
1975     numBytes =
1976         (TMShortCard) ((size_t) ((oldXlations ? oldXlations->numStateTrees : 0)
1977                                  +
1978                                  newXlations->numStateTrees) *
1979                        sizeof(TMComplexBindProcsRec));
1980     newBindings = (TMComplexBindProcs) XtStackAlloc(numBytes, stackBindings);
1981     XtBZero((char *) newBindings, numBytes);
1982 
1983     if (operation == XtTableUnmerge) {
1984         newTable = UnmergeTranslations(dest,
1985                                        oldXlations,
1986                                        newXlations,
1987                                        0,
1988                                        oldBindings, numOldBindings,
1989                                        newBindings, &numNewBindings);
1990 #ifdef DEBUG
1991         /* check for no match for unmerge */
1992         if (newTable == oldXlations) {
1993             XtWarning("attempt to unmerge invalid table");
1994             XtStackFree((char *) newBindings, (char *) stackBindings);
1995             return (newTable != NULL);
1996         }
1997 #endif                          /* DEBUG */
1998     }
1999     else {
2000         newTable = MergeTranslations(dest,
2001                                      oldXlations,
2002                                      accNewXlations,
2003                                      operation,
2004                                      source,
2005                                      oldBindings, newBindings, &numNewBindings);
2006     }
2007     if (XtIsRealized(dest)) {
2008         oldMask = 0;
2009         if (oldXlations)
2010             oldMask = oldXlations->eventMask;
2011         _XtUninstallTranslations(dest);
2012     }
2013 
2014     dest->core.tm.proc_table =
2015         (XtActionProc *) MakeBindData(newBindings, numNewBindings, bindData);
2016 
2017     XtFree((char *) bindData);
2018 
2019     dest->core.tm.translations = newTable;
2020 
2021     if (XtIsRealized(dest)) {
2022         EventMask mask = 0;
2023 
2024         _XtInstallTranslations(dest);
2025         if (newTable)
2026             mask = newTable->eventMask;
2027         if (mask != oldMask)
2028             XSelectInput(XtDisplay(dest), XtWindow(dest),
2029                          (long) XtBuildEventMask(dest));
2030     }
2031     XtStackFree((XtPointer) newBindings, (XtPointer) stackBindings);
2032     return (newTable != NULL);
2033 }
2034 
2035 /*
2036  * If a GetValues is done on a translation resource that contains
2037  * accelerators we need to return the accelerator context in addition
2038  * to the pure translations.  Since this means returning memory that
2039  * the client controlls but we still own, we will track the "headers"
2040  * that we return (via a linked list pointed to from the bindData) and
2041  * free it at destroy time.
2042  */
2043 XtTranslations
_XtGetTranslationValue(Widget w)2044 _XtGetTranslationValue(Widget w)
2045 {
2046     XtTM tmRecPtr = (XtTM) &w->core.tm;
2047     ATranslations *aXlationsPtr;
2048     TMComplexBindData cBindData = (TMComplexBindData) tmRecPtr->proc_table;
2049     XtTranslations xlations = tmRecPtr->translations;
2050 
2051     if (!xlations || !cBindData || !cBindData->isComplex)
2052         return xlations;
2053 
2054     /* Walk the list looking to see if we already have generated a
2055      * header for the currently installed translations.  If we have,
2056      * just return that header.  Otherwise create a new header.
2057      */
2058     for (aXlationsPtr = (ATranslations *) &cBindData->accel_context;
2059          *aXlationsPtr && (*aXlationsPtr)->xlations != xlations;
2060          aXlationsPtr = &(*aXlationsPtr)->next);
2061     if (*aXlationsPtr)
2062         return (XtTranslations) *aXlationsPtr;
2063     else {
2064         /* create a new aXlations context */
2065         ATranslations aXlations;
2066         Cardinal numBindings = xlations->numStateTrees;
2067 
2068         (*aXlationsPtr) = aXlations = (ATranslations)
2069             __XtMalloc((Cardinal) (sizeof(ATranslationData) +
2070                                    (numBindings -
2071                                     1) * sizeof(TMComplexBindProcsRec)));
2072 
2073         aXlations->hasBindings = True;
2074         aXlations->xlations = xlations;
2075         aXlations->next = NULL;
2076         XtMemmove((char *) &aXlations->bindTbl[0],
2077                   (char *) &cBindData->bindTbl[0],
2078                   numBindings * sizeof(TMComplexBindProcsRec));
2079         return (XtTranslations) aXlations;
2080     }
2081 }
2082 
2083 static void
RemoveStateTree(TMStateTree tree _X_UNUSED)2084 RemoveStateTree(TMStateTree tree _X_UNUSED)
2085 {
2086 #ifdef REFCNT_TRANSLATIONS
2087     TMComplexStateTree stateTree = (TMComplexStateTree) tree;
2088 
2089     if (--stateTree->refCount == 0) {
2090         /*
2091          * should we free/refcount the match recs ?
2092          */
2093         if (!stateTree->isSimple) {
2094             StatePtr currState, nextState;
2095             TMShortCard i;
2096 
2097             for (i = 0; i < stateTree->numComplexBranchHeads; i++) {
2098                 currState = nextState = stateTree->complexBranchHeadTbl[i];
2099                 for (; nextState;) {
2100                     FreeActions(currState->actions);
2101                     currState->actions = NULL;
2102                     if (!currState->isCycleEnd)
2103                         nextState = currState->nextLevel;
2104                     else
2105                         nextState = NULL;
2106                     XtFree((char *) currState);
2107                 }
2108             }
2109             XtFree((char *) stateTree->complexBranchHeadTbl);
2110         }
2111         XtFree((char *) stateTree->branchHeadTbl);
2112         XtFree((char *) stateTree);
2113     }
2114 #endif                          /* REFCNT_TRANSLATIONS */
2115 }
2116 
2117 void
_XtRemoveStateTreeByIndex(XtTranslations xlations,TMShortCard i)2118 _XtRemoveStateTreeByIndex(XtTranslations xlations, TMShortCard i)
2119 {
2120     TMStateTree *stateTrees = xlations->stateTreeTbl;
2121 
2122     RemoveStateTree(stateTrees[i]);
2123     xlations->numStateTrees--;
2124 
2125     for (; i < xlations->numStateTrees; i++) {
2126         stateTrees[i] = stateTrees[i + 1];
2127     }
2128 }
2129 
2130 void
_XtFreeTranslations(XtAppContext app,XrmValuePtr toVal,XtPointer closure _X_UNUSED,XrmValuePtr args _X_UNUSED,Cardinal * num_args)2131 _XtFreeTranslations(XtAppContext app,
2132                     XrmValuePtr toVal,
2133                     XtPointer closure _X_UNUSED,
2134                     XrmValuePtr args _X_UNUSED,
2135                     Cardinal *num_args)
2136 {
2137     XtTranslations xlations;
2138     int i;
2139 
2140     if (*num_args != 0)
2141         XtAppWarningMsg(app,
2142                         "invalidParameters", "freeTranslations",
2143                         XtCXtToolkitError,
2144                         "Freeing XtTranslations requires no extra arguments",
2145                         NULL, NULL);
2146 
2147     xlations = *(XtTranslations *) toVal->addr;
2148     for (i = 0; i < (int) xlations->numStateTrees; i++)
2149         RemoveStateTree(xlations->stateTreeTbl[i]);
2150     XtFree((char *) xlations);
2151 }
2152 
2153 /*  The spec is not clear on when actions specified in accelerators are bound;
2154  *  Bind them at Realize the same as translations
2155  */
2156 void
XtInstallAccelerators(Widget destination,Widget source)2157 XtInstallAccelerators(Widget destination, Widget source)
2158 {
2159     XtTranslations aXlations;
2160     _XtTranslateOp op;
2161 
2162     WIDGET_TO_APPCON(destination);
2163 
2164     /*
2165      * test that it was parsed as an accelarator table. Even though
2166      * there doesn't need to be a distinction it makes life easier if
2167      * we honor the spec implication that aXlations is an accelerator
2168      */
2169     LOCK_APP(app);
2170     LOCK_PROCESS;
2171     if ((!XtIsWidget(source)) ||
2172         ((aXlations = source->core.accelerators) == NULL) ||
2173         (aXlations->stateTreeTbl[0]->simple.isAccelerator == False)) {
2174         UNLOCK_PROCESS;
2175         UNLOCK_APP(app);
2176         return;
2177     }
2178 
2179     aXlations = source->core.accelerators;
2180     op = aXlations->operation;
2181 
2182     if (ComposeTranslations(destination, op, source, aXlations) &&
2183         (XtClass(source)->core_class.display_accelerator != NULL)) {
2184         _XtString buf = _XtPrintXlations(destination, aXlations, source, False);
2185 
2186         (*(XtClass(source)->core_class.display_accelerator)) (source, buf);
2187         XtFree(buf);
2188     }
2189     UNLOCK_PROCESS;
2190     UNLOCK_APP(app);
2191 }
2192 
2193 void
XtInstallAllAccelerators(Widget destination,Widget source)2194 XtInstallAllAccelerators(Widget destination, Widget source)
2195 {
2196     Cardinal i;
2197 
2198     WIDGET_TO_APPCON(destination);
2199 
2200     /* Recurse down normal children */
2201     LOCK_APP(app);
2202     LOCK_PROCESS;
2203     if (XtIsComposite(source)) {
2204         CompositeWidget cw = (CompositeWidget) source;
2205 
2206         for (i = 0; i < cw->composite.num_children; i++) {
2207             XtInstallAllAccelerators(destination, cw->composite.children[i]);
2208         }
2209     }
2210 
2211     /* Recurse down popup children */
2212     if (XtIsWidget(source)) {
2213         for (i = 0; i < source->core.num_popups; i++) {
2214             XtInstallAllAccelerators(destination, source->core.popup_list[i]);
2215         }
2216     }
2217     /* Finally, apply procedure to this widget */
2218     XtInstallAccelerators(destination, source);
2219     UNLOCK_PROCESS;
2220     UNLOCK_APP(app);
2221 }
2222 
2223 #if 0                           /* dead code */
2224 static _XtTranslateOp
2225 _XtGetTMOperation(XtTranslations xlations)
2226 {
2227     return ((xlations->hasBindings)
2228             ? ((ATranslations) xlations)->xlations->operation
2229             : xlations->operation);
2230 }
2231 #endif
2232 
2233 void
XtAugmentTranslations(Widget widget,XtTranslations new)2234 XtAugmentTranslations(Widget widget, XtTranslations new)
2235 {
2236     Widget hookobj;
2237 
2238     WIDGET_TO_APPCON(widget);
2239 
2240     LOCK_APP(app);
2241     LOCK_PROCESS;
2242     (void) ComposeTranslations(widget, XtTableAugment, (Widget) NULL, new);
2243     hookobj = XtHooksOfDisplay(XtDisplayOfObject(widget));
2244     if (XtHasCallbacks(hookobj, XtNchangeHook) == XtCallbackHasSome) {
2245         XtChangeHookDataRec call_data;
2246 
2247         call_data.type = XtHaugmentTranslations;
2248         call_data.widget = widget;
2249         XtCallCallbackList(hookobj,
2250                            ((HookObject) hookobj)->hooks.changehook_callbacks,
2251                            (XtPointer) &call_data);
2252     }
2253     UNLOCK_PROCESS;
2254     UNLOCK_APP(app);
2255 }
2256 
2257 void
XtOverrideTranslations(Widget widget,XtTranslations new)2258 XtOverrideTranslations(Widget widget, XtTranslations new)
2259 {
2260     Widget hookobj;
2261 
2262     WIDGET_TO_APPCON(widget);
2263 
2264     LOCK_APP(app);
2265     LOCK_PROCESS;
2266     (void) ComposeTranslations(widget, XtTableOverride, (Widget) NULL, new);
2267     hookobj = XtHooksOfDisplay(XtDisplayOfObject(widget));
2268     if (XtHasCallbacks(hookobj, XtNchangeHook) == XtCallbackHasSome) {
2269         XtChangeHookDataRec call_data;
2270 
2271         call_data.type = XtHoverrideTranslations;
2272         call_data.widget = widget;
2273         XtCallCallbackList(hookobj,
2274                            ((HookObject) hookobj)->hooks.changehook_callbacks,
2275                            (XtPointer) &call_data);
2276     }
2277     UNLOCK_PROCESS;
2278     UNLOCK_APP(app);
2279 }
2280 
2281 void
_XtMergeTranslations(Widget widget,XtTranslations newXlations,_XtTranslateOp op)2282 _XtMergeTranslations(Widget widget,
2283                      XtTranslations newXlations,
2284                      _XtTranslateOp op)
2285 {
2286     if (!newXlations) {
2287         if (!widget->core.tm.translations)
2288             return;
2289         else {
2290             newXlations = widget->core.tm.translations;
2291             widget->core.tm.translations = NULL;
2292         }
2293     }
2294     (void) ComposeTranslations(widget, op, (Widget) NULL, newXlations);
2295 }
2296 
2297 void
_XtUnmergeTranslations(Widget widget,XtTranslations xlations)2298 _XtUnmergeTranslations(Widget widget, XtTranslations xlations)
2299 {
2300     ComposeTranslations(widget, XtTableUnmerge, (Widget) NULL, xlations);
2301 }
2302