1 /*
2 
3   Copyright (c) 2003-2013 uim Project https://github.com/uim/uim
4 
5   All rights reserved.
6 
7   Redistribution and use in source and binary forms, with or without
8   modification, are permitted provided that the following conditions
9   are met:
10 
11   1. Redistributions of source code must retain the above copyright
12      notice, this list of conditions and the following disclaimer.
13   2. Redistributions in binary form must reproduce the above copyright
14      notice, this list of conditions and the following disclaimer in the
15      documentation and/or other materials provided with the distribution.
16   3. Neither the name of authors nor the names of its contributors
17      may be used to endorse or promote products derived from this software
18      without specific prior written permission.
19 
20   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
21   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
24   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30   SUCH DAMAGE.
31 */
32 #include <config.h>
33 
34 #include "quiminputcontext_compose.h"
35 
36 #include <cctype>
37 #include <cerrno>
38 #include <clocale>
39 #include <cstdio>
40 #include <cstdlib>
41 #include <cstring>
42 #include <sys/stat.h>
43 #include <sys/param.h>
44 #ifdef HAVE_LANGINFO_CODESET
45 #include <langinfo.h>
46 #endif
47 
48 #include <QtCore/QTextCodec>
49 #if QT_VERSION >= 0x050000
50 # undef Expose
51 #endif
52 #include <QtGui/QKeyEvent>
53 
54 #include <X11/Xlib.h>
55 #include <X11/Xutil.h>
56 #include <X11/keysym.h>
57 
58 #include "uim/uim.h"
59 
60 #if QT_VERSION < 0x050000
61 #include "quiminputcontext.h"
62 #else
63 #include <quimplatforminputcontext.h>
64 #endif
65 
66 static const char COMPOSE_FILE[] = "Compose";
67 static const char COMPOSE_DIR_FILE[] = "X11/locale/compose.dir";
68 static const char XLOCALE_DIR[] = "X11/locale";
69 static const char FALLBACK_XLIB_DIR[] = "/usr/X11R6/lib";
70 #define XLC_BUFSIZE        256
iscomment(char ch)71 static inline bool iscomment(char ch)
72 {
73     return ch == '#' || ch == '\0';
74 }
75 
76 
77 static int parse_line(char *line, char **argv, int argsize);
78 static unsigned int KeySymToUcs4(KeySym keysym);
79 
80 #if QT_VERSION < 0x050000
Compose(DefTree * top,QUimInputContext * ic)81 Compose::Compose(DefTree *top, QUimInputContext *ic)
82 #else
83 Compose::Compose(DefTree *top, QUimPlatformInputContext *ic)
84 #endif
85 {
86     m_ic = ic;
87     m_composed = 0;
88     m_top = top;
89     m_context = top;
90 }
91 
~Compose()92 Compose::~Compose()
93 {
94 }
95 
handle_qkey(const QKeyEvent * event)96 bool Compose::handle_qkey(const QKeyEvent *event)
97 {
98     int qstate = event->modifiers();
99     unsigned int xstate = 0;
100     if (qstate & Qt::ShiftModifier)
101         xstate |= ShiftMask;
102     if (qstate & Qt::ControlModifier)
103         xstate |= ControlMask;
104     if (qstate & Qt::AltModifier)
105         xstate |= Mod1Mask; // XXX
106     if (qstate & Qt::MetaModifier)
107         xstate |= Mod1Mask; // XXX
108 
109     int qkey = event->key();
110     unsigned int xkeysym;
111     if (qkey >= 0x20 && qkey <= 0xff) {
112         if (isascii(qkey) && isprint(qkey)) {
113             QString str = event->text();
114             int ascii = str.length() ? str.unicode()->toLatin1() : 0;
115             if (isalpha(ascii))
116                     xkeysym = ascii;
117             else
118                 if ((qstate & Qt::ControlModifier) &&
119                     (ascii >= 0x01 && ascii <= 0x1a))
120                     if (qstate & Qt::ShiftModifier)
121                         xkeysym = ascii + 0x40;
122                     else
123                         xkeysym = ascii + 0x60;
124                 else
125                         xkeysym = qkey;
126         } else {
127             xkeysym = qkey;
128         }
129     } else if (qkey >= Qt::Key_Dead_Grave && qkey <= Qt::Key_Dead_Horn) {
130         xkeysym = qkey + 0xec00 - 0x01000000;
131     } else {
132         switch (qkey) {
133         case Qt::Key_Escape: xkeysym = XK_Escape; break;
134         case Qt::Key_Tab: xkeysym = XK_Tab; break;
135         case Qt::Key_Backspace: xkeysym = XK_BackSpace; break;
136         case Qt::Key_Return: xkeysym = XK_Return; break;
137         case Qt::Key_Insert: xkeysym = XK_Insert; break;
138         case Qt::Key_Delete: xkeysym = XK_Delete; break;
139         case Qt::Key_Pause: xkeysym = XK_Pause; break;
140         case Qt::Key_Print: xkeysym = XK_Print; break;
141         case Qt::Key_SysReq: xkeysym = XK_Sys_Req; break;
142         case Qt::Key_Clear: xkeysym = XK_Clear; break;
143         case Qt::Key_Home: xkeysym = XK_Home; break;
144         case Qt::Key_End: xkeysym = XK_End; break;
145         case Qt::Key_Left: xkeysym = XK_Left; break;
146         case Qt::Key_Up: xkeysym = XK_Up; break;
147         case Qt::Key_Right: xkeysym = XK_Right; break;
148         case Qt::Key_Down: xkeysym = XK_Down; break;
149         case Qt::Key_PageUp: xkeysym = XK_Prior; break;
150         case Qt::Key_PageDown: xkeysym = XK_Next; break;
151         case Qt::Key_Shift: xkeysym = XK_Shift_L; break;
152         case Qt::Key_Control: xkeysym = XK_Control_L; break;
153         case Qt::Key_Meta: xkeysym = XK_Meta_L; break;
154         case Qt::Key_Alt: xkeysym = XK_Alt_L; break;
155         case Qt::Key_CapsLock: xkeysym = XK_Caps_Lock; break;
156         case Qt::Key_NumLock: xkeysym = XK_Num_Lock; break;
157         case Qt::Key_ScrollLock: xkeysym = XK_Scroll_Lock; break;
158         case Qt::Key_F1: xkeysym = XK_F1; break;
159         case Qt::Key_F2: xkeysym = XK_F2; break;
160         case Qt::Key_F3: xkeysym = XK_F3; break;
161         case Qt::Key_F4: xkeysym = XK_F4; break;
162         case Qt::Key_F5: xkeysym = XK_F5; break;
163         case Qt::Key_F6: xkeysym = XK_F6; break;
164         case Qt::Key_F7: xkeysym = XK_F7; break;
165         case Qt::Key_F8: xkeysym = XK_F8; break;
166         case Qt::Key_F9: xkeysym = XK_F9; break;
167         case Qt::Key_F10: xkeysym = XK_F10; break;
168         case Qt::Key_F11: xkeysym = XK_F11; break;
169         case Qt::Key_F12: xkeysym = XK_F12; break;
170         case Qt::Key_F13: xkeysym = XK_F13; break;
171         case Qt::Key_F14: xkeysym = XK_F14; break;
172         case Qt::Key_F15: xkeysym = XK_F15; break;
173         case Qt::Key_F16: xkeysym = XK_F16; break;
174         case Qt::Key_F17: xkeysym = XK_F17; break;
175         case Qt::Key_F18: xkeysym = XK_F18; break;
176         case Qt::Key_F19: xkeysym = XK_F19; break;
177         case Qt::Key_F20: xkeysym = XK_F20; break;
178         case Qt::Key_F21: xkeysym = XK_F21; break;
179         case Qt::Key_F22: xkeysym = XK_F22; break;
180         case Qt::Key_F23: xkeysym = XK_F23; break;
181         case Qt::Key_F24: xkeysym = XK_F24; break;
182         case Qt::Key_F25: xkeysym = XK_F25; break;
183         case Qt::Key_F26: xkeysym = XK_F26; break;
184         case Qt::Key_F27: xkeysym = XK_F27; break;
185         case Qt::Key_F28: xkeysym = XK_F28; break;
186         case Qt::Key_F29: xkeysym = XK_F29; break;
187         case Qt::Key_F30: xkeysym = XK_F30; break;
188         case Qt::Key_F31: xkeysym = XK_F31; break;
189         case Qt::Key_F32: xkeysym = XK_F32; break;
190         case Qt::Key_F33: xkeysym = XK_F33; break;
191         case Qt::Key_F34: xkeysym = XK_F34; break;
192         case Qt::Key_F35: xkeysym = XK_F35; break;
193         case Qt::Key_Super_L: xkeysym = XK_Super_L; break;
194         case Qt::Key_Super_R: xkeysym = XK_Super_R; break;
195         case Qt::Key_Menu: xkeysym = XK_Menu; break;
196         case Qt::Key_Hyper_L: xkeysym = XK_Hyper_L; break;
197         case Qt::Key_Hyper_R: xkeysym = XK_Hyper_R; break;
198         case Qt::Key_Help: xkeysym = XK_Help; break;
199         case Qt::Key_Multi_key: xkeysym = XK_Multi_key; break;
200         case Qt::Key_Codeinput: xkeysym = XK_Codeinput; break;
201         case Qt::Key_SingleCandidate: xkeysym = XK_SingleCandidate; break;
202         case Qt::Key_PreviousCandidate: xkeysym = XK_PreviousCandidate; break;
203         case Qt::Key_Mode_switch: xkeysym = XK_Mode_switch; break;
204         case Qt::Key_Kanji: xkeysym = XK_Kanji; break;
205         case Qt::Key_Muhenkan: xkeysym = XK_Muhenkan; break;
206         case Qt::Key_Henkan: xkeysym = XK_Henkan_Mode; break;
207         case Qt::Key_Romaji: xkeysym = XK_Romaji; break;
208         case Qt::Key_Hiragana: xkeysym = XK_Hiragana; break;
209         case Qt::Key_Katakana: xkeysym = XK_Katakana; break;
210         case Qt::Key_Hiragana_Katakana: xkeysym = XK_Hiragana_Katakana; break;
211         case Qt::Key_Zenkaku: xkeysym = XK_Zenkaku; break;
212         case Qt::Key_Hankaku: xkeysym = XK_Hankaku; break;
213         case Qt::Key_Zenkaku_Hankaku: xkeysym = XK_Zenkaku_Hankaku; break;
214         case Qt::Key_Touroku: xkeysym = XK_Touroku; break;
215         case Qt::Key_Massyo: xkeysym = XK_Massyo; break;
216         case Qt::Key_Kana_Lock: xkeysym = XK_Kana_Lock; break;
217         case Qt::Key_Kana_Shift: xkeysym = XK_Kana_Shift; break;
218         case Qt::Key_Eisu_Shift: xkeysym = XK_Eisu_Shift; break;
219         case Qt::Key_Eisu_toggle: xkeysym = XK_Eisu_toggle; break;
220 
221         case Qt::Key_Hangul: xkeysym = XK_Hangul; break;
222         case Qt::Key_Hangul_Start: xkeysym = XK_Hangul_Start; break;
223         case Qt::Key_Hangul_End: xkeysym = XK_Hangul_End; break;
224         case Qt::Key_Hangul_Jamo: xkeysym = XK_Hangul_Jamo; break;
225         case Qt::Key_Hangul_Romaja: xkeysym = XK_Hangul_Romaja; break;
226         case Qt::Key_Hangul_Jeonja: xkeysym = XK_Hangul_Jeonja; break;
227         case Qt::Key_Hangul_Banja: xkeysym = XK_Hangul_Banja; break;
228         case Qt::Key_Hangul_PreHanja: xkeysym = XK_Hangul_PreHanja; break;
229         case Qt::Key_Hangul_PostHanja: xkeysym = XK_Hangul_PostHanja; break;
230         case Qt::Key_Hangul_Special: xkeysym = XK_Hangul_Special; break;
231         default: xkeysym = qkey; break;
232         }
233     }
234 
235     int type = event->type();
236     bool press = (type == QEvent::KeyPress);
237     return handleKey(xkeysym, xstate, press);
238 }
239 
handleKey(KeySym xkeysym,int xkeystate,bool is_push)240 bool Compose::handleKey(KeySym xkeysym, int xkeystate, bool is_push)
241 {
242     if ((is_push == false)  || m_top == 0)
243         return false;
244 
245     if (IsModifierKey(xkeysym))
246         return false;
247 
248     DefTree *p;
249     for (p = m_context; p ; p = p->next) {
250         if (((xkeystate & p->modifier_mask) == p->modifier) &&
251                 (xkeysym == p->keysym)) {
252             break;
253         }
254     }
255 
256     if (p) { // Matched
257         if (p->succession) { // Intermediate
258             m_context = p->succession;
259             return true;
260         } else { // Terminate (reached to leaf)
261             m_composed = p;
262             // commit string here
263             m_ic->commitString(QString::fromUtf8(m_composed->utf8));
264             // initialize internal state for next key sequence
265             m_context = m_top;
266             return true;
267         }
268     } else { // Unmatched
269         if (m_context == m_top)
270             return false;
271         // Error (Sequence Unmatch occurred)
272         m_context = m_top;
273         return true;
274     }
275 }
276 
reset()277 void Compose::reset()
278 {
279     m_context = m_top;
280     m_composed = 0;
281 }
282 
283 static int
nextch(FILE * fp,int * lastch)284 nextch(FILE *fp, int *lastch)
285 {
286     int c;
287 
288     if (*lastch != 0) {
289         c = *lastch;
290         *lastch = 0;
291     } else {
292         c = getc(fp);
293         if (c == '\\') {
294             c = getc(fp);
295             if (c == '\n') {
296                 c = getc(fp);
297             } else {
298                 ungetc(c, fp);
299                 c = '\\';
300             }
301         }
302     }
303     return(c);
304 }
305 
306 static void
putbackch(int c,int * lastch)307 putbackch(int c, int *lastch)
308 {
309     *lastch = c;
310 }
311 
312 
313 static const int ENDOFFILE = 0;
314 static const int ENDOFLINE = 1;
315 static const int COLON = 2;
316 static const int LESS = 3;
317 static const int GREATER = 4;
318 static const int EXCLAM = 5;
319 static const int TILDE = 6;
320 static const int STRING = 7;
321 static const int KEY = 8;
322 static const int ERROR = 9;
323 
324 
325 #ifndef isalnum
isalnum(char c)326 static inline bool isalnum(char c)
327 {
328      return ('0' <= c && c <= '9') ||
329          ('A' <= c && c <= 'Z') ||
330          ('a' <= c && c <= 'z');
331 }
332 #endif
333 
334 static int
nexttoken(FILE * fp,char ** tokenbuf,int * lastch,size_t * buflen)335 nexttoken(FILE *fp, char **tokenbuf, int *lastch, size_t *buflen)
336 {
337     int c;
338     while ((c = nextch(fp, lastch)) == ' ' || c == '\t') {
339     }
340 
341     int token;
342     char *p;
343     int i;
344     size_t len = 0;
345     switch (c) {
346     case EOF:
347         token = ENDOFFILE;
348         break;
349     case '\n':
350         token = ENDOFLINE;
351         break;
352     case '<':
353         token = LESS;
354         break;
355     case '>':
356         token = GREATER;
357         break;
358     case ':':
359         token = COLON;
360         break;
361     case '!':
362         token = EXCLAM;
363         break;
364     case '~':
365         token = TILDE;
366         break;
367     case '"':
368         p = *tokenbuf;
369         while ((c = nextch(fp, lastch)) != '"') {
370             if (len >= *buflen - 1) {
371                     *buflen += BUFSIZ;
372                     *tokenbuf = (char *)realloc(*tokenbuf, *buflen);
373                     p = *tokenbuf + len;
374             }
375             if (c == '\n' || c == EOF) {
376                 putbackch(c, lastch);
377                 token = ERROR;
378                 goto string_error;
379             } else if (c == '\\') {
380                 c = nextch(fp, lastch);
381                 switch (c) {
382                 case '\\':
383                 case '"':
384                     *p++ = static_cast<char>(c);
385                     len++;
386                     break;
387                 case 'n':
388                     *p++ = '\n';
389                     len++;
390                     break;
391                 case 'r':
392                     *p++ = '\r';
393                     len++;
394                     break;
395                 case 't':
396                     *p++ = '\t';
397                     len++;
398                     break;
399                 case '0':
400                 case '1':
401                 case '2':
402                 case '3':
403                 case '4':
404                 case '5':
405                 case '6':
406                 case '7':
407                     i = c - '0';
408                     c = nextch(fp, lastch);
409                     for (int j = 0; j < 2 && c >= '0' && c <= '7'; j++) {
410                         i <<= 3;
411                         i += c - '0';
412                         c = nextch(fp, lastch);
413                     }
414                     putbackch(c, lastch);
415                     *p++ = static_cast<char>(i);
416                     len++;
417                     break;
418                 case 'X':
419                 case 'x':
420                     i = 0;
421                     int j;
422                     for (j = 0; j < 2; j++) {
423                         c = nextch(fp, lastch);
424                         i <<= 4;
425                         if (c >= '0' && c <= '9') {
426                             i += c - '0';
427                         } else if (c >= 'A' && c <= 'F') {
428                             i += c - 'A' + 10;
429                         } else if (c >= 'a' && c <= 'f') {
430                             i += c - 'a' + 10;
431                         } else {
432                             putbackch(c, lastch);
433                             i >>= 4;
434                             break;
435                         }
436                     }
437                     if (j == 0) {
438                         token = ERROR;
439                         goto string_error;
440                     }
441                     *p++ = static_cast<char>(i);
442                     len++;
443                     break;
444                 case EOF:
445                     putbackch(c, lastch);
446                     token = ERROR;
447                     goto string_error;
448                 default:
449                     *p++ = static_cast<char>(c);
450                     len++;
451                     break;
452                 }
453             } else {
454                 *p++ = static_cast<char>(c);
455                 len++;
456             }
457         }
458         *p = '\0';
459         token = STRING;
460         break;
461     case '#':
462         while ((c = nextch(fp, lastch)) != '\n' && c != EOF) {
463         }
464         if (c == '\n') {
465             token = ENDOFLINE;
466         } else {
467             token = ENDOFFILE;
468         }
469         break;
470     default:
471         if (isalnum(c) || c == '_' || c == '-') {
472             if (len >= *buflen - 1) {
473                 *buflen += BUFSIZ;
474                 *tokenbuf = (char *)realloc(*tokenbuf,  *buflen);
475             }
476             p = *tokenbuf;
477             *p++ = static_cast<char>(c);
478             len++;
479             c = nextch(fp, lastch);
480             while (isalnum(c) || c == '_' || c == '-') {
481                 if (len >= *buflen - 1) {
482                         *buflen += BUFSIZ;
483                         *tokenbuf = (char *)realloc(*tokenbuf,  *buflen);
484                         p = *tokenbuf + len;
485                 }
486                 *p++ = static_cast<char>(c);
487                 len++;
488                 c = nextch(fp, lastch);
489             }
490             *p = '\0';
491             putbackch(c, lastch);
492             token = KEY;
493         } else {
494             token = ERROR;
495         }
496         break;
497     }
498 string_error:
499     return(token);
500 }
501 
502 static long
modmask(char * name)503 modmask(char *name)
504 {
505     struct _modtbl {
506         const char *name;
507         long mask;
508     };
509 
510     static struct _modtbl tbl[] = {
511         { "Ctrl",       ControlMask     },
512         { "Lock",       LockMask        },
513         { "Caps",       LockMask        },
514         { "Shift",      ShiftMask       },
515         { "Alt",        Mod1Mask        },
516         { "Meta",       Mod1Mask        },
517         { 0,            0               }};
518 
519     long mask = 0;
520     for (struct _modtbl *p = tbl; p->name != 0; p++) {
521         if (strcmp(name, p->name) == 0) {
522             mask = p->mask;
523             break;
524         }
525     }
526     return(mask);
527 }
528 
529 #if defined(Q_WS_X11)
530 int
TransFileName(char * transname,const char * name,size_t len)531 QUimInputContext::TransFileName(char *transname, const char *name, size_t len)
532 {
533     char *home = 0;
534     char lcCompose[MAXPATHLEN];
535     const char *i = name;
536     char ret[MAXPATHLEN];
537     char *j = ret;
538     lcCompose[0] = ret[0] = '\0';
539     while (*i && j - ret < MAXPATHLEN - 1) {
540         if (*i == '%') {
541             i++;
542             switch (*i) {
543             case '%':
544                 *j++ = '%';
545                 break;
546             case 'H':
547                 home = getenv("HOME");
548                 if (home) {
549                     strlcat(ret, home, sizeof(ret));
550                     j += strlen(home);
551                 }
552                 break;
553             case 'L':
554                 get_compose_filename(lcCompose, sizeof(lcCompose));
555                 if (lcCompose[0] != '\0') {
556                     strlcat(ret, lcCompose, sizeof(ret));
557                     j += strlen(lcCompose);
558                 }
559                 break;
560             }
561             i++;
562         } else {
563             *j++ = *i++;
564         }
565         *j = '\0';
566     }
567     strlcpy(transname, ret, len);
568 
569     return 1;
570 }
571 
572 #ifndef MB_LEN_MAX
573 #define MB_LEN_MAX 6
574 #endif
575 
576 const char *
get_encoding()577 QUimInputContext::get_encoding()
578 {
579 #if 0
580     // starting from Qt 4.2.0, QTextCodec::codecForLocale()->name()
581     // returns "System"
582     QTextCodec *codec = QTextCodec::codecForLocale();
583 
584     return codec->name();
585 #else
586 
587 #ifdef HAVE_LANGINFO_CODESET
588     return nl_langinfo(CODESET);
589 #else
590     char *p = setlocale(LC_CTYPE, 0);
591     if (p)
592         p = strchr(p, '.');
593 
594     return p ? p + 1 : "UTF-8";
595 #endif
596 
597 #endif
598 }
599 
600 int
get_lang_region(char * locale,size_t len)601 QUimInputContext::get_lang_region(char *locale, size_t len)
602 {
603     strlcpy(locale, setlocale(LC_CTYPE, 0), len);
604     if (locale[0] == '\0') {
605         return 0;
606     }
607 
608     char *p = strrchr(locale, '.');
609     if (p)
610         *p = '\0';
611 
612     return 1;
613 }
614 
615 int
get_mb_string(char * buf,unsigned int ks)616 QUimInputContext::get_mb_string(char *buf, unsigned int ks)
617 {
618     unsigned int ucs = KeySymToUcs4(ks);
619     QString qs = QString(QChar(ucs));
620     const char *mb = qs.toLocal8Bit().data();
621     if (!mb)
622         return 0;
623     int len = strlen(mb);
624     strlcpy(buf, mb, MB_LEN_MAX + 1);
625 
626     return len;
627 }
628 
629 static const unsigned AllMask = (ShiftMask | LockMask | ControlMask | Mod1Mask);
630 #define SEQUENCE_MAX    10
631 
632 int
parse_compose_line(FILE * fp,char ** tokenbuf,size_t * buflen)633 QUimInputContext::parse_compose_line(FILE *fp, char **tokenbuf, size_t *buflen)
634 {
635     struct DefBuffer {
636         unsigned modifier_mask;
637         unsigned modifier;
638         KeySym keysym;
639     };
640 
641     int token;
642     int lastch = 0;
643     do {
644         token = nexttoken(fp, tokenbuf, &lastch, buflen);
645     } while (token == ENDOFLINE);
646 
647     if (token == ENDOFFILE) {
648         return(-1);
649     }
650 
651     unsigned modifier_mask;
652     unsigned modifier;
653     unsigned tmp;
654     KeySym keysym = NoSymbol;
655     Bool exclam;
656     Bool tilde;
657     KeySym rhs_keysym = 0;
658     char *rhs_string_mb;
659     int l;
660     char local_mb_buf[MB_LEN_MAX + 1];
661     struct DefBuffer buf[SEQUENCE_MAX];
662     int n = 0;
663 
664     DefTree **top = &mTreeTop;
665     DefTree *p = 0;
666     do {
667         if ((token == KEY) && (strcmp("include", *tokenbuf) == 0)) {
668             char filename[MAXPATHLEN];
669             FILE *infp;
670             token = nexttoken(fp, tokenbuf, &lastch, buflen);
671             if (token != KEY && token != STRING)
672                 goto error;
673 
674             if (!TransFileName(filename, *tokenbuf, sizeof(filename)) || filename[0] == '\0')
675                 goto error;
676             infp = fopen(filename, "r");
677             if (infp == 0)
678                 goto error;
679             ParseComposeStringFile(infp);
680             fclose(infp);
681             return 0;
682         } else if ((token == KEY) && (strcmp("None", *tokenbuf) == 0)) {
683             modifier = 0;
684             modifier_mask = AllMask;
685             token = nexttoken(fp, tokenbuf, &lastch, buflen);
686         } else {
687             modifier_mask = modifier = 0;
688             exclam = False;
689             if (token == EXCLAM) {
690                 exclam = True;
691                 token = nexttoken(fp, tokenbuf, &lastch, buflen);
692             }
693             while (token == TILDE || token == KEY) {
694                 tilde = False;
695                 if (token == TILDE) {
696                     tilde = True;
697                     token = nexttoken(fp, tokenbuf, &lastch, buflen);
698                     if (token != KEY)
699                         goto error;
700                 }
701                 tmp = modmask(*tokenbuf);
702                 if (!tmp) {
703                     goto error;
704                 }
705                 modifier_mask |= tmp;
706                 if (tilde) {
707                     modifier &= ~tmp;
708                 } else {
709                     modifier |= tmp;
710                 }
711                 token = nexttoken(fp, tokenbuf, &lastch, buflen);
712             }
713             if (exclam) {
714                 modifier_mask = AllMask;
715 
716             }
717         }
718 
719         if (token != LESS) {
720             goto error;
721         }
722 
723         token = nexttoken(fp, tokenbuf, &lastch, buflen);
724         if (token != KEY) {
725             goto error;
726         }
727 
728         token = nexttoken(fp, tokenbuf, &lastch, buflen);
729         if (token != GREATER) {
730             goto error;
731         }
732 
733         keysym = XStringToKeysym(*tokenbuf);
734         if (keysym == NoSymbol) {
735             goto error;
736         }
737 
738         buf[n].keysym = keysym;
739         buf[n].modifier = modifier;
740         buf[n].modifier_mask = modifier_mask;
741         n++;
742         if (n >= SEQUENCE_MAX)
743             goto error;
744         token = nexttoken(fp, tokenbuf, &lastch, buflen);
745     } while (token != COLON);
746 
747     token = nexttoken(fp, tokenbuf, &lastch, buflen);
748     if (token == STRING) {
749         if ((rhs_string_mb = (char *)malloc(strlen(*tokenbuf) + 1)) == 0)
750             goto error;
751         strcpy(rhs_string_mb, *tokenbuf);
752         token = nexttoken(fp, tokenbuf, &lastch, buflen);
753         if (token == KEY) {
754             rhs_keysym = XStringToKeysym(*tokenbuf);
755             if (rhs_keysym == NoSymbol) {
756                 free(rhs_string_mb);
757                 goto error;
758             }
759             token = nexttoken(fp, tokenbuf, &lastch, buflen);
760         }
761         if (token != ENDOFLINE && token != ENDOFFILE) {
762             free(rhs_string_mb);
763             goto error;
764         }
765     } else if (token == KEY) {
766         rhs_keysym = XStringToKeysym(*tokenbuf);
767         if (rhs_keysym == NoSymbol) {
768             goto error;
769         }
770         token = nexttoken(fp, tokenbuf, &lastch, buflen);
771         if (token != ENDOFLINE && token != ENDOFFILE) {
772             goto error;
773         }
774 
775         l = get_mb_string(local_mb_buf, rhs_keysym);
776         if (l == 0) {
777             rhs_string_mb = (char *)malloc(1);
778         } else {
779             rhs_string_mb = (char *)malloc(l + 1);
780         }
781         if (rhs_string_mb == 0) {
782             goto error;
783         }
784         memcpy(rhs_string_mb, local_mb_buf, l);
785         rhs_string_mb[l] = '\0';
786     } else {
787         goto error;
788     }
789 
790     for (int i = 0; i < n; i++) {
791         for (p = *top; p; p = p->next) {
792             if (buf[i].keysym == p->keysym &&
793                 buf[i].modifier == p->modifier &&
794                 buf[i].modifier_mask == p->modifier_mask) {
795                 break;
796             }
797         }
798         if (p) {
799             top = &p->succession;
800         } else {
801             if ((p = (DefTree*)malloc(sizeof(DefTree))) == 0) {
802                 free(rhs_string_mb);
803                 goto error;
804             }
805             p->keysym = buf[i].keysym;
806             p->modifier = buf[i].modifier;
807             p->modifier_mask = buf[i].modifier_mask;
808             p->succession = 0;
809             p->next = *top;
810             p->mb = 0;
811             p->utf8 = 0;
812             p->ks = NoSymbol;
813             *top = p;
814             top = &p->succession;
815         }
816     }
817 
818     free(p->mb);
819     p->mb = rhs_string_mb;
820     free(p->utf8);
821     {
822         QTextCodec *codec = QTextCodec::codecForLocale();
823         QString qs = codec->toUnicode(rhs_string_mb);
824         char *rhs_string_utf8 = strdup(qs.toUtf8().data());
825         p->utf8 = rhs_string_utf8;
826     }
827     p->ks = rhs_keysym;
828     return n;
829 error:
830     while (token != ENDOFLINE && token != ENDOFFILE) {
831         token = nexttoken(fp, tokenbuf, &lastch, buflen);
832     }
833     return 0;
834 }
835 
836 void
FreeComposeTree(DefTree * top)837 QUimInputContext::FreeComposeTree(DefTree *top)
838 {
839     if (!top)
840         return;
841 
842     if (top->succession)
843         FreeComposeTree(top->succession);
844     if (top->next)
845         FreeComposeTree(top->next);
846     free(top->mb);
847     free(top->utf8);
848     free(top);
849 }
850 
851 void
ParseComposeStringFile(FILE * fp)852 QUimInputContext::ParseComposeStringFile(FILE *fp)
853 {
854     char *tbp, *p[1];
855     struct stat st;
856     size_t buflen = BUFSIZ;
857 
858     if (fstat(fileno(fp), &st) != -1 && S_ISREG(st.st_mode) &&
859         st.st_size > 0) {
860 
861         tbp = (char *)malloc(buflen);
862         p[0] = tbp;
863         if (tbp != 0) {
864             while (parse_compose_line(fp, p, &buflen) >= 0) {
865             }
866             free(p[0]);
867         }
868     }
869 }
870 
create_compose_tree()871 void QUimInputContext::create_compose_tree()
872 {
873     char *compose_env = getenv("XCOMPOSEFILE");
874     char name[MAXPATHLEN];
875     name[0] = '\0';
876     FILE *fp = 0;
877     if (compose_env != 0) {
878         strlcpy(name, compose_env, sizeof(name));
879     } else {
880         char *home = getenv("HOME");
881         if (home != 0) {
882             snprintf(name, sizeof(name), "%s/.XCompose", home);
883             fp = fopen(name, "r");
884             if (fp == 0)
885                 name[0] = '\0';
886         }
887     }
888 
889     if (name[0] == '\0' && !get_compose_filename(name, sizeof(name))) {
890         if (fp)
891             fclose(fp);
892         return;
893     }
894 
895     if (fp == 0 && ((fp = fopen(name, "r")) == 0))
896         return;
897 
898     char lang_region[BUFSIZ];
899     int ret = get_lang_region(lang_region, sizeof(lang_region));
900     const char *encoding = get_encoding();
901     if (!ret || encoding == 0) {
902         fprintf(stderr, "Warning: locale name is NULL\n");
903         fclose(fp);
904         return;
905     }
906 
907     ParseComposeStringFile(fp);
908     fclose(fp);
909 }
910 
get_compose_filename(char * filename,size_t len)911 int QUimInputContext::get_compose_filename(char *filename, size_t len)
912 {
913     char lang_region[BUFSIZ];
914     int ret = get_lang_region(lang_region, sizeof(lang_region));
915     const char *encoding = get_encoding();
916 
917     if (!ret || encoding == 0)
918         return 0;
919 
920     char locale[BUFSIZ];
921     snprintf(locale, sizeof(locale), "%s.%s", lang_region, encoding);
922     char compose_dir_file[MAXPATHLEN];
923     const char *xlib_dir = XLIB_DIR ;
924     snprintf(compose_dir_file, sizeof(compose_dir_file), "%s/%s", XLIB_DIR, COMPOSE_DIR_FILE);
925 
926     FILE *fp = fopen(compose_dir_file, "r");
927     if (fp == 0) {
928         /* retry with fallback file */
929         if (strcmp(FALLBACK_XLIB_DIR, XLIB_DIR)) {
930             snprintf(compose_dir_file, sizeof(compose_dir_file), "%s/%s",
931                             FALLBACK_XLIB_DIR, COMPOSE_DIR_FILE);
932             fp = fopen(compose_dir_file, "r");
933             if (fp == 0)
934                 return 0;
935             xlib_dir = FALLBACK_XLIB_DIR;
936         } else
937             return 0;
938     }
939 
940     char name[MAXPATHLEN];
941     name[0] = '\0';
942     char buf[XLC_BUFSIZE];
943     while (fgets(buf, XLC_BUFSIZE, fp) != 0) {
944         char *p = buf;
945         int n;
946         char *args[2], *from, *to;
947         // isspace for tab doesn't seem to work with Qt4...
948         while (static_cast<unsigned char>(isspace(*p)) || *p == '\t') {
949             ++p;
950         }
951         if (iscomment(*p)) {
952             continue;
953         }
954         n = parse_line(p, args, 2);
955         if (n != 2) {
956             continue;
957         }
958         from = args[1], to = args[0];
959         if (!strcmp(from, locale)) {
960             strlcpy(name, to, sizeof(name));
961             break;
962         }
963     }
964     fclose(fp);
965 
966     if (name[0] == '\0')
967         return 0;
968 
969     snprintf(filename, len, "%s/%s/%s", xlib_dir, XLOCALE_DIR, name);
970 
971     return 1;
972 }
973 #endif
974 
975 static int
parse_line(char * line,char ** argv,int argsize)976 parse_line(char *line, char **argv, int argsize)
977 {
978     int argc = 0;
979     char *p = line;
980 
981     while (argc < argsize) {
982         // isspace for tab doesn't seem to work with Qt4...
983         while (static_cast<unsigned char>(isspace(*p)) || *p == '\t') {
984             ++p;
985         }
986         if (*p == '\0') {
987             break;
988         }
989         argv[argc++] = p;
990         while (*p != ':' && *p != '\n' && *p != '\0') {
991             ++p;
992         }
993         if (*p == '\0') {
994             break;
995         }
996         *p++ = '\0';
997     }
998 
999     return argc;
1000 }
1001 
1002 static unsigned short const keysym_to_unicode_1a1_1ff[] = {
1003             0x0104, 0x02d8, 0x0141, 0x0000, 0x013d, 0x015a, 0x0000, /* 0x01a0-0x01a7 */
1004     0x0000, 0x0160, 0x015e, 0x0164, 0x0179, 0x0000, 0x017d, 0x017b, /* 0x01a8-0x01af */
1005     0x0000, 0x0105, 0x02db, 0x0142, 0x0000, 0x013e, 0x015b, 0x02c7, /* 0x01b0-0x01b7 */
1006     0x0000, 0x0161, 0x015f, 0x0165, 0x017a, 0x02dd, 0x017e, 0x017c, /* 0x01b8-0x01bf */
1007     0x0154, 0x0000, 0x0000, 0x0102, 0x0000, 0x0139, 0x0106, 0x0000, /* 0x01c0-0x01c7 */
1008     0x010c, 0x0000, 0x0118, 0x0000, 0x011a, 0x0000, 0x0000, 0x010e, /* 0x01c8-0x01cf */
1009     0x0110, 0x0143, 0x0147, 0x0000, 0x0000, 0x0150, 0x0000, 0x0000, /* 0x01d0-0x01d7 */
1010     0x0158, 0x016e, 0x0000, 0x0170, 0x0000, 0x0000, 0x0162, 0x0000, /* 0x01d8-0x01df */
1011     0x0155, 0x0000, 0x0000, 0x0103, 0x0000, 0x013a, 0x0107, 0x0000, /* 0x01e0-0x01e7 */
1012     0x010d, 0x0000, 0x0119, 0x0000, 0x011b, 0x0000, 0x0000, 0x010f, /* 0x01e8-0x01ef */
1013     0x0111, 0x0144, 0x0148, 0x0000, 0x0000, 0x0151, 0x0000, 0x0000, /* 0x01f0-0x01f7 */
1014     0x0159, 0x016f, 0x0000, 0x0171, 0x0000, 0x0000, 0x0163, 0x02d9  /* 0x01f8-0x01ff */
1015 };
1016 
1017 static unsigned short const keysym_to_unicode_2a1_2fe[] = {
1018             0x0126, 0x0000, 0x0000, 0x0000, 0x0000, 0x0124, 0x0000, /* 0x02a0-0x02a7 */
1019     0x0000, 0x0130, 0x0000, 0x011e, 0x0134, 0x0000, 0x0000, 0x0000, /* 0x02a8-0x02af */
1020     0x0000, 0x0127, 0x0000, 0x0000, 0x0000, 0x0000, 0x0125, 0x0000, /* 0x02b0-0x02b7 */
1021     0x0000, 0x0131, 0x0000, 0x011f, 0x0135, 0x0000, 0x0000, 0x0000, /* 0x02b8-0x02bf */
1022     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x010a, 0x0108, 0x0000, /* 0x02c0-0x02c7 */
1023     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x02c8-0x02cf */
1024     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0120, 0x0000, 0x0000, /* 0x02d0-0x02d7 */
1025     0x011c, 0x0000, 0x0000, 0x0000, 0x0000, 0x016c, 0x015c, 0x0000, /* 0x02d8-0x02df */
1026     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x010b, 0x0109, 0x0000, /* 0x02e0-0x02e7 */
1027     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x02e8-0x02ef */
1028     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0121, 0x0000, 0x0000, /* 0x02f0-0x02f7 */
1029     0x011d, 0x0000, 0x0000, 0x0000, 0x0000, 0x016d, 0x015d          /* 0x02f8-0x02ff */
1030 };
1031 
1032 static unsigned short const keysym_to_unicode_3a2_3fe[] = {
1033                     0x0138, 0x0156, 0x0000, 0x0128, 0x013b, 0x0000, /* 0x03a0-0x03a7 */
1034     0x0000, 0x0000, 0x0112, 0x0122, 0x0166, 0x0000, 0x0000, 0x0000, /* 0x03a8-0x03af */
1035     0x0000, 0x0000, 0x0000, 0x0157, 0x0000, 0x0129, 0x013c, 0x0000, /* 0x03b0-0x03b7 */
1036     0x0000, 0x0000, 0x0113, 0x0123, 0x0167, 0x014a, 0x0000, 0x014b, /* 0x03b8-0x03bf */
1037     0x0100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x012e, /* 0x03c0-0x03c7 */
1038     0x0000, 0x0000, 0x0000, 0x0000, 0x0116, 0x0000, 0x0000, 0x012a, /* 0x03c8-0x03cf */
1039     0x0000, 0x0145, 0x014c, 0x0136, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x03d0-0x03d7 */
1040     0x0000, 0x0172, 0x0000, 0x0000, 0x0000, 0x0168, 0x016a, 0x0000, /* 0x03d8-0x03df */
1041     0x0101, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x012f, /* 0x03e0-0x03e7 */
1042     0x0000, 0x0000, 0x0000, 0x0000, 0x0117, 0x0000, 0x0000, 0x012b, /* 0x03e8-0x03ef */
1043     0x0000, 0x0146, 0x014d, 0x0137, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x03f0-0x03f7 */
1044     0x0000, 0x0173, 0x0000, 0x0000, 0x0000, 0x0169, 0x016b          /* 0x03f8-0x03ff */
1045 };
1046 
1047 static unsigned short const keysym_to_unicode_4a1_4df[] = {
1048             0x3002, 0x3008, 0x3009, 0x3001, 0x30fb, 0x30f2, 0x30a1, /* 0x04a0-0x04a7 */
1049     0x30a3, 0x30a5, 0x30a7, 0x30a9, 0x30e3, 0x30e5, 0x30e7, 0x30c3, /* 0x04a8-0x04af */
1050     0x30fc, 0x30a2, 0x30a4, 0x30a6, 0x30a8, 0x30aa, 0x30ab, 0x30ad, /* 0x04b0-0x04b7 */
1051     0x30af, 0x30b1, 0x30b3, 0x30b5, 0x30b7, 0x30b9, 0x30bb, 0x30bd, /* 0x04b8-0x04bf */
1052     0x30bf, 0x30c1, 0x30c4, 0x30c6, 0x30c8, 0x30ca, 0x30cb, 0x30cc, /* 0x04c0-0x04c7 */
1053     0x30cd, 0x30ce, 0x30cf, 0x30d2, 0x30d5, 0x30d8, 0x30db, 0x30de, /* 0x04c8-0x04cf */
1054     0x30df, 0x30e0, 0x30e1, 0x30e2, 0x30e4, 0x30e6, 0x30e8, 0x30e9, /* 0x04d0-0x04d7 */
1055     0x30ea, 0x30eb, 0x30ec, 0x30ed, 0x30ef, 0x30f3, 0x309b, 0x309c  /* 0x04d8-0x04df */
1056 };
1057 
1058 static unsigned short const keysym_to_unicode_590_5fe[] = {
1059     0x06f0, 0x06f1, 0x06f2, 0x06f3, 0x06f4, 0x06f5, 0x06f6, 0x06f7, /* 0x0590-0x0597 */
1060     0x06f8, 0x06f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x0598-0x059f */
1061     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x066a, 0x0670, 0x0679, /* 0x05a0-0x05a7 */
1062 
1063     0x067e, 0x0686, 0x0688, 0x0691, 0x060c, 0x0000, 0x06d4, 0x0000, /* 0x05ac-0x05af */
1064     0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, /* 0x05b0-0x05b7 */
1065     0x0668, 0x0669, 0x0000, 0x061b, 0x0000, 0x0000, 0x0000, 0x061f, /* 0x05b8-0x05bf */
1066     0x0000, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, /* 0x05c0-0x05c7 */
1067     0x0628, 0x0629, 0x062a, 0x062b, 0x062c, 0x062d, 0x062e, 0x062f, /* 0x05c8-0x05cf */
1068     0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, /* 0x05d0-0x05d7 */
1069     0x0638, 0x0639, 0x063a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x05d8-0x05df */
1070     0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, /* 0x05e0-0x05e7 */
1071     0x0648, 0x0649, 0x064a, 0x064b, 0x064c, 0x064d, 0x064e, 0x064f, /* 0x05e8-0x05ef */
1072     0x0650, 0x0651, 0x0652, 0x0653, 0x0654, 0x0655, 0x0698, 0x06a4, /* 0x05f0-0x05f7 */
1073     0x06a9, 0x06af, 0x06ba, 0x06be, 0x06cc, 0x06d2, 0x06c1          /* 0x05f8-0x05fe */
1074 };
1075 
1076 static unsigned short keysym_to_unicode_680_6ff[] = {
1077     0x0492, 0x0496, 0x049a, 0x049c, 0x04a2, 0x04ae, 0x04b0, 0x04b2, /* 0x0680-0x0687 */
1078     0x04b6, 0x04b8, 0x04ba, 0x0000, 0x04d8, 0x04e2, 0x04e8, 0x04ee, /* 0x0688-0x068f */
1079     0x0493, 0x0497, 0x049b, 0x049d, 0x04a3, 0x04af, 0x04b1, 0x04b3, /* 0x0690-0x0697 */
1080     0x04b7, 0x04b9, 0x04bb, 0x0000, 0x04d9, 0x04e3, 0x04e9, 0x04ef, /* 0x0698-0x069f */
1081     0x0000, 0x0452, 0x0453, 0x0451, 0x0454, 0x0455, 0x0456, 0x0457, /* 0x06a0-0x06a7 */
1082     0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x0491, 0x045e, 0x045f, /* 0x06a8-0x06af */
1083     0x2116, 0x0402, 0x0403, 0x0401, 0x0404, 0x0405, 0x0406, 0x0407, /* 0x06b0-0x06b7 */
1084     0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x0490, 0x040e, 0x040f, /* 0x06b8-0x06bf */
1085     0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, /* 0x06c0-0x06c7 */
1086     0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, /* 0x06c8-0x06cf */
1087     0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, /* 0x06d0-0x06d7 */
1088     0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a, /* 0x06d8-0x06df */
1089     0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, /* 0x06e0-0x06e7 */
1090     0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, /* 0x06e8-0x06ef */
1091     0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, /* 0x06f0-0x06f7 */
1092     0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a  /* 0x06f8-0x06ff */
1093 };
1094 
1095 static unsigned short const keysym_to_unicode_7a1_7f9[] = {
1096             0x0386, 0x0388, 0x0389, 0x038a, 0x03aa, 0x0000, 0x038c, /* 0x07a0-0x07a7 */
1097     0x038e, 0x03ab, 0x0000, 0x038f, 0x0000, 0x0000, 0x0385, 0x2015, /* 0x07a8-0x07af */
1098     0x0000, 0x03ac, 0x03ad, 0x03ae, 0x03af, 0x03ca, 0x0390, 0x03cc, /* 0x07b0-0x07b7 */
1099     0x03cd, 0x03cb, 0x03b0, 0x03ce, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x07b8-0x07bf */
1100     0x0000, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, /* 0x07c0-0x07c7 */
1101     0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, /* 0x07c8-0x07cf */
1102     0x03a0, 0x03a1, 0x03a3, 0x0000, 0x03a4, 0x03a5, 0x03a6, 0x03a7, /* 0x07d0-0x07d7 */
1103     0x03a8, 0x03a9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x07d8-0x07df */
1104     0x0000, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, /* 0x07e0-0x07e7 */
1105     0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, /* 0x07e8-0x07ef */
1106     0x03c0, 0x03c1, 0x03c3, 0x03c2, 0x03c4, 0x03c5, 0x03c6, 0x03c7, /* 0x07f0-0x07f7 */
1107     0x03c8, 0x03c9                                                  /* 0x07f8-0x07ff */
1108 };
1109 
1110 static unsigned short const keysym_to_unicode_8a4_8fe[] = {
1111                                     0x2320, 0x2321, 0x0000, 0x231c, /* 0x08a0-0x08a7 */
1112     0x231d, 0x231e, 0x231f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x08a8-0x08af */
1113     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x08b0-0x08b7 */
1114     0x0000, 0x0000, 0x0000, 0x0000, 0x2264, 0x2260, 0x2265, 0x222b, /* 0x08b8-0x08bf */
1115     0x2234, 0x0000, 0x221e, 0x0000, 0x0000, 0x2207, 0x0000, 0x0000, /* 0x08c0-0x08c7 */
1116     0x2245, 0x2246, 0x0000, 0x0000, 0x0000, 0x0000, 0x22a2, 0x0000, /* 0x08c8-0x08cf */
1117     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x221a, 0x0000, /* 0x08d0-0x08d7 */
1118     0x0000, 0x0000, 0x2282, 0x2283, 0x2229, 0x222a, 0x2227, 0x2228, /* 0x08d8-0x08df */
1119     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x08e0-0x08e7 */
1120     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x08e8-0x08ef */
1121     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0192, 0x0000, /* 0x08f0-0x08f7 */
1122     0x0000, 0x0000, 0x0000, 0x2190, 0x2191, 0x2192, 0x2193          /* 0x08f8-0x08ff */
1123 };
1124 
1125 static unsigned short const keysym_to_unicode_9df_9f8[] = {
1126                                                             0x2422, /* 0x09d8-0x09df */
1127     0x2666, 0x25a6, 0x2409, 0x240c, 0x240d, 0x240a, 0x0000, 0x0000, /* 0x09e0-0x09e7 */
1128     0x240a, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x2500, /* 0x09e8-0x09ef */
1129     0x0000, 0x0000, 0x0000, 0x0000, 0x251c, 0x2524, 0x2534, 0x252c, /* 0x09f0-0x09f7 */
1130     0x2502                                                          /* 0x09f8-0x09ff */
1131 };
1132 
1133 static unsigned short const keysym_to_unicode_aa1_afe[] = {
1134             0x2003, 0x2002, 0x2004, 0x2005, 0x2007, 0x2008, 0x2009, /* 0x0aa0-0x0aa7 */
1135     0x200a, 0x2014, 0x2013, 0x0000, 0x0000, 0x0000, 0x2026, 0x2025, /* 0x0aa8-0x0aaf */
1136     0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a, /* 0x0ab0-0x0ab7 */
1137     0x2105, 0x0000, 0x0000, 0x2012, 0x2039, 0x2024, 0x203a, 0x0000, /* 0x0ab8-0x0abf */
1138     0x0000, 0x0000, 0x0000, 0x215b, 0x215c, 0x215d, 0x215e, 0x0000, /* 0x0ac0-0x0ac7 */
1139     0x0000, 0x2122, 0x2120, 0x0000, 0x25c1, 0x25b7, 0x25cb, 0x25ad, /* 0x0ac8-0x0acf */
1140     0x2018, 0x2019, 0x201c, 0x201d, 0x211e, 0x0000, 0x2032, 0x2033, /* 0x0ad0-0x0ad7 */
1141     0x0000, 0x271d, 0x0000, 0x220e, 0x25c2, 0x2023, 0x25cf, 0x25ac, /* 0x0ad8-0x0adf */
1142     0x25e6, 0x25ab, 0x25ae, 0x25b5, 0x25bf, 0x2606, 0x2022, 0x25aa, /* 0x0ae0-0x0ae7 */
1143     0x25b4, 0x25be, 0x261a, 0x261b, 0x2663, 0x2666, 0x2665, 0x0000, /* 0x0ae8-0x0aef */
1144     0x2720, 0x2020, 0x2021, 0x2713, 0x2612, 0x266f, 0x266d, 0x2642, /* 0x0af0-0x0af7 */
1145     0x2640, 0x2121, 0x2315, 0x2117, 0x2038, 0x201a, 0x201e          /* 0x0af8-0x0aff */
1146 };
1147 
1148 /* none of the APL keysyms match the Unicode characters */
1149 
1150 static unsigned short const keysym_to_unicode_cdf_cfa[] = {
1151                                                             0x2017, /* 0x0cd8-0x0cdf */
1152     0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, 0x05d7, /* 0x0ce0-0x0ce7 */
1153     0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05dd, 0x05de, 0x05df, /* 0x0ce8-0x0cef */
1154     0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e4, 0x05e5, 0x05e6, 0x05e7, /* 0x0cf0-0x0cf7 */
1155     0x05e8, 0x05e9, 0x05ea                                          /* 0x0cf8-0x0cff */
1156 };
1157 
1158 static unsigned short const keysym_to_unicode_da1_df9[] = {
1159             0x0e01, 0x0e02, 0x0e03, 0x0e04, 0x0e05, 0x0e06, 0x0e07, /* 0x0da0-0x0da7 */
1160     0x0e08, 0x0e09, 0x0e0a, 0x0e0b, 0x0e0c, 0x0e0d, 0x0e0e, 0x0e0f, /* 0x0da8-0x0daf */
1161     0x0e10, 0x0e11, 0x0e12, 0x0e13, 0x0e14, 0x0e15, 0x0e16, 0x0e17, /* 0x0db0-0x0db7 */
1162     0x0e18, 0x0e19, 0x0e1a, 0x0e1b, 0x0e1c, 0x0e1d, 0x0e1e, 0x0e1f, /* 0x0db8-0x0dbf */
1163     0x0e20, 0x0e21, 0x0e22, 0x0e23, 0x0e24, 0x0e25, 0x0e26, 0x0e27, /* 0x0dc0-0x0dc7 */
1164     0x0e28, 0x0e29, 0x0e2a, 0x0e2b, 0x0e2c, 0x0e2d, 0x0e2e, 0x0e2f, /* 0x0dc8-0x0dcf */
1165     0x0e30, 0x0e31, 0x0e32, 0x0e33, 0x0e34, 0x0e35, 0x0e36, 0x0e37, /* 0x0dd0-0x0dd7 */
1166     0x0e38, 0x0e39, 0x0e3a, 0x0000, 0x0000, 0x0000, 0x0e3e, 0x0e3f, /* 0x0dd8-0x0ddf */
1167     0x0e40, 0x0e41, 0x0e42, 0x0e43, 0x0e44, 0x0e45, 0x0e46, 0x0e47, /* 0x0de0-0x0de7 */
1168     0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c, 0x0e4d, 0x0000, 0x0000, /* 0x0de8-0x0def */
1169     0x0e50, 0x0e51, 0x0e52, 0x0e53, 0x0e54, 0x0e55, 0x0e56, 0x0e57, /* 0x0df0-0x0df7 */
1170     0x0e58, 0x0e59                                                  /* 0x0df8-0x0dff */
1171 };
1172 
1173 static unsigned short const keysym_to_unicode_ea0_eff[] = {
1174     0x0000, 0x1101, 0x1101, 0x11aa, 0x1102, 0x11ac, 0x11ad, 0x1103, /* 0x0ea0-0x0ea7 */
1175     0x1104, 0x1105, 0x11b0, 0x11b1, 0x11b2, 0x11b3, 0x11b4, 0x11b5, /* 0x0ea8-0x0eaf */
1176     0x11b6, 0x1106, 0x1107, 0x1108, 0x11b9, 0x1109, 0x110a, 0x110b, /* 0x0eb0-0x0eb7 */
1177     0x110c, 0x110d, 0x110e, 0x110f, 0x1110, 0x1111, 0x1112, 0x1161, /* 0x0eb8-0x0ebf */
1178     0x1162, 0x1163, 0x1164, 0x1165, 0x1166, 0x1167, 0x1168, 0x1169, /* 0x0ec0-0x0ec7 */
1179     0x116a, 0x116b, 0x116c, 0x116d, 0x116e, 0x116f, 0x1170, 0x1171, /* 0x0ec8-0x0ecf */
1180     0x1172, 0x1173, 0x1174, 0x1175, 0x11a8, 0x11a9, 0x11aa, 0x11ab, /* 0x0ed0-0x0ed7 */
1181     0x11ac, 0x11ad, 0x11ae, 0x11af, 0x11b0, 0x11b1, 0x11b2, 0x11b3, /* 0x0ed8-0x0edf */
1182     0x11b4, 0x11b5, 0x11b6, 0x11b7, 0x11b8, 0x11b9, 0x11ba, 0x11bb, /* 0x0ee0-0x0ee7 */
1183     0x11bc, 0x11bd, 0x11be, 0x11bf, 0x11c0, 0x11c1, 0x11c2, 0x0000, /* 0x0ee8-0x0eef */
1184     0x0000, 0x0000, 0x1140, 0x0000, 0x0000, 0x1159, 0x119e, 0x0000, /* 0x0ef0-0x0ef7 */
1185     0x11eb, 0x0000, 0x11f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x20a9, /* 0x0ef8-0x0eff */
1186 };
1187 
1188 static unsigned short keysym_to_unicode_12a1_12fe[] = {
1189             0x1e02, 0x1e03, 0x0000, 0x0000, 0x0000, 0x1e0a, 0x0000, /* 0x12a0-0x12a7 */
1190     0x1e80, 0x0000, 0x1e82, 0x1e0b, 0x1ef2, 0x0000, 0x0000, 0x0000, /* 0x12a8-0x12af */
1191     0x1e1e, 0x1e1f, 0x0000, 0x0000, 0x1e40, 0x1e41, 0x0000, 0x1e56, /* 0x12b0-0x12b7 */
1192     0x1e81, 0x1e57, 0x1e83, 0x1e60, 0x1ef3, 0x1e84, 0x1e85, 0x1e61, /* 0x12b8-0x12bf */
1193     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x12c0-0x12c7 */
1194     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x12c8-0x12cf */
1195     0x0174, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1e6a, /* 0x12d0-0x12d7 */
1196     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0176, 0x0000, /* 0x12d8-0x12df */
1197     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x12e0-0x12e7 */
1198     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x12e8-0x12ef */
1199     0x0175, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1e6b, /* 0x12f0-0x12f7 */
1200     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0177          /* 0x12f0-0x12ff */
1201 };
1202 
1203 static unsigned short const keysym_to_unicode_13bc_13be[] = {
1204                                     0x0152, 0x0153, 0x0178          /* 0x13b8-0x13bf */
1205 };
1206 
1207 static unsigned short keysym_to_unicode_14a1_14ff[] = {
1208             0x2741, 0x00a7, 0x0589, 0x0029, 0x0028, 0x00bb, 0x00ab, /* 0x14a0-0x14a7 */
1209     0x2014, 0x002e, 0x055d, 0x002c, 0x2013, 0x058a, 0x2026, 0x055c, /* 0x14a8-0x14af */
1210     0x055b, 0x055e, 0x0531, 0x0561, 0x0532, 0x0562, 0x0533, 0x0563, /* 0x14b0-0x14b7 */
1211     0x0534, 0x0564, 0x0535, 0x0565, 0x0536, 0x0566, 0x0537, 0x0567, /* 0x14b8-0x14bf */
1212     0x0538, 0x0568, 0x0539, 0x0569, 0x053a, 0x056a, 0x053b, 0x056b, /* 0x14c0-0x14c7 */
1213     0x053c, 0x056c, 0x053d, 0x056d, 0x053e, 0x056e, 0x053f, 0x056f, /* 0x14c8-0x14cf */
1214     0x0540, 0x0570, 0x0541, 0x0571, 0x0542, 0x0572, 0x0543, 0x0573, /* 0x14d0-0x14d7 */
1215     0x0544, 0x0574, 0x0545, 0x0575, 0x0546, 0x0576, 0x0547, 0x0577, /* 0x14d8-0x14df */
1216     0x0548, 0x0578, 0x0549, 0x0579, 0x054a, 0x057a, 0x054b, 0x057b, /* 0x14e0-0x14e7 */
1217     0x054c, 0x057c, 0x054d, 0x057d, 0x054e, 0x057e, 0x054f, 0x057f, /* 0x14e8-0x14ef */
1218     0x0550, 0x0580, 0x0551, 0x0581, 0x0552, 0x0582, 0x0553, 0x0583, /* 0x14f0-0x14f7 */
1219     0x0554, 0x0584, 0x0555, 0x0585, 0x0556, 0x0586, 0x2019, 0x0027, /* 0x14f8-0x14ff */
1220 };
1221 
1222 static unsigned short keysym_to_unicode_15d0_15f6[] = {
1223     0x10d0, 0x10d1, 0x10d2, 0x10d3, 0x10d4, 0x10d5, 0x10d6, 0x10d7, /* 0x15d0-0x15d7 */
1224     0x10d8, 0x10d9, 0x10da, 0x10db, 0x10dc, 0x10dd, 0x10de, 0x10df, /* 0x15d8-0x15df */
1225     0x10e0, 0x10e1, 0x10e2, 0x10e3, 0x10e4, 0x10e5, 0x10e6, 0x10e7, /* 0x15e0-0x15e7 */
1226     0x10e8, 0x10e9, 0x10ea, 0x10eb, 0x10ec, 0x10ed, 0x10ee, 0x10ef, /* 0x15e8-0x15ef */
1227     0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6          /* 0x15f0-0x15f7 */
1228 };
1229 
1230 static unsigned short keysym_to_unicode_16a0_16f6[] = {
1231     0x0000, 0x0000, 0xf0a2, 0x1e8a, 0x0000, 0xf0a5, 0x012c, 0xf0a7, /* 0x16a0-0x16a7 */
1232     0xf0a8, 0x01b5, 0x01e6, 0x0000, 0x0000, 0x0000, 0x0000, 0x019f, /* 0x16a8-0x16af */
1233     0x0000, 0x0000, 0xf0b2, 0x1e8b, 0x01d1, 0xf0b5, 0x012d, 0xf0b7, /* 0x16b0-0x16b7 */
1234     0xf0b8, 0x01b6, 0x01e7, 0x0000, 0x0000, 0x01d2, 0x0000, 0x0275, /* 0x16b8-0x16bf */
1235     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x018f, 0x0000, /* 0x16c0-0x16c7 */
1236     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x16c8-0x16cf */
1237     0x0000, 0x1e36, 0xf0d2, 0xf0d3, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x16d0-0x16d7 */
1238     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x16d8-0x16df */
1239     0x0000, 0x1e37, 0xf0e2, 0xf0e3, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x16e0-0x16e7 */
1240     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x16e8-0x16ef */
1241     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0259          /* 0x16f0-0x16f6 */
1242 };
1243 
1244 static unsigned short const keysym_to_unicode_1e9f_1eff[] = {
1245                                                             0x0303,
1246     0x1ea0, 0x1ea1, 0x1ea2, 0x1ea3, 0x1ea4, 0x1ea5, 0x1ea6, 0x1ea7, /* 0x1ea0-0x1ea7 */
1247     0x1ea8, 0x1ea9, 0x1eaa, 0x1eab, 0x1eac, 0x1ead, 0x1eae, 0x1eaf, /* 0x1ea8-0x1eaf */
1248     0x1eb0, 0x1eb1, 0x1eb2, 0x1eb3, 0x1eb4, 0x1eb5, 0x1eb6, 0x1eb7, /* 0x1eb0-0x1eb7 */
1249     0x1eb8, 0x1eb9, 0x1eba, 0x1ebb, 0x1ebc, 0x1ebd, 0x1ebe, 0x1ebf, /* 0x1eb8-0x1ebf */
1250     0x1ec0, 0x1ec1, 0x1ec2, 0x1ec3, 0x1ec4, 0x1ec5, 0x1ec6, 0x1ec7, /* 0x1ec0-0x1ec7 */
1251     0x1ec8, 0x1ec9, 0x1eca, 0x1ecb, 0x1ecc, 0x1ecd, 0x1ece, 0x1ecf, /* 0x1ec8-0x1ecf */
1252     0x1ed0, 0x1ed1, 0x1ed2, 0x1ed3, 0x1ed4, 0x1ed5, 0x1ed6, 0x1ed7, /* 0x1ed0-0x1ed7 */
1253     0x1ed8, 0x1ed9, 0x1eda, 0x1edb, 0x1edc, 0x1edd, 0x1ede, 0x1edf, /* 0x1ed8-0x1edf */
1254     0x1ee0, 0x1ee1, 0x1ee2, 0x1ee3, 0x1ee4, 0x1ee5, 0x1ee6, 0x1ee7, /* 0x1ee0-0x1ee7 */
1255     0x1ee8, 0x1ee9, 0x1eea, 0x1eeb, 0x1eec, 0x1eed, 0x1eee, 0x1eef, /* 0x1ee8-0x1eef */
1256     0x1ef0, 0x1ef1, 0x0300, 0x0301, 0x1ef4, 0x1ef5, 0x1ef6, 0x1ef7, /* 0x1ef0-0x1ef7 */
1257     0x1ef8, 0x1ef9, 0x01a0, 0x01a1, 0x01af, 0x01b0, 0x0309, 0x0323  /* 0x1ef8-0x1eff */
1258 };
1259 
1260 static unsigned short const keysym_to_unicode_20a0_20ac[] = {
1261     0x20a0, 0x20a1, 0x20a2, 0x20a3, 0x20a4, 0x20a5, 0x20a6, 0x20a7, /* 0x20a0-0x20a7 */
1262     0x20a8, 0x20a9, 0x20aa, 0x20ab, 0x20ac                          /* 0x20a8-0x20af */
1263 };
1264 
1265 static unsigned int
KeySymToUcs4(KeySym keysym)1266 KeySymToUcs4(KeySym keysym)
1267 {
1268     /* 'Unicode keysym' */
1269     if ((keysym & 0xff000000) == 0x01000000)
1270         return (keysym & 0x00ffffff);
1271 
1272     if (keysym > 0 && keysym < 0x100)
1273         return keysym;
1274     else if (keysym > 0x1a0 && keysym < 0x200)
1275         return keysym_to_unicode_1a1_1ff[keysym - 0x1a1];
1276     else if (keysym > 0x2a0 && keysym < 0x2ff)
1277         return keysym_to_unicode_2a1_2fe[keysym - 0x2a1];
1278     else if (keysym > 0x3a1 && keysym < 0x3ff)
1279         return keysym_to_unicode_3a2_3fe[keysym - 0x3a2];
1280     else if (keysym > 0x4a0 && keysym < 0x4e0)
1281         return keysym_to_unicode_4a1_4df[keysym - 0x4a1];
1282     else if (keysym > 0x589 && keysym < 0x5ff)
1283         return keysym_to_unicode_590_5fe[keysym - 0x590];
1284     else if (keysym > 0x67f && keysym < 0x700)
1285         return keysym_to_unicode_680_6ff[keysym - 0x680];
1286     else if (keysym > 0x7a0 && keysym < 0x7fa)
1287         return keysym_to_unicode_7a1_7f9[keysym - 0x7a1];
1288     else if (keysym > 0x8a3 && keysym < 0x8ff)
1289         return keysym_to_unicode_8a4_8fe[keysym - 0x8a4];
1290     else if (keysym > 0x9de && keysym < 0x9f9)
1291         return keysym_to_unicode_9df_9f8[keysym - 0x9df];
1292     else if (keysym > 0xaa0 && keysym < 0xaff)
1293         return keysym_to_unicode_aa1_afe[keysym - 0xaa1];
1294     else if (keysym > 0xcde && keysym < 0xcfb)
1295         return keysym_to_unicode_cdf_cfa[keysym - 0xcdf];
1296     else if (keysym > 0xda0 && keysym < 0xdfa)
1297         return keysym_to_unicode_da1_df9[keysym - 0xda1];
1298     else if (keysym > 0xe9f && keysym < 0xf00)
1299         return keysym_to_unicode_ea0_eff[keysym - 0xea0];
1300     else if (keysym > 0x12a0 && keysym < 0x12ff)
1301         return keysym_to_unicode_12a1_12fe[keysym - 0x12a1];
1302     else if (keysym > 0x13bb && keysym < 0x13bf)
1303         return keysym_to_unicode_13bc_13be[keysym - 0x13bc];
1304     else if (keysym > 0x14a0 && keysym < 0x1500)
1305         return keysym_to_unicode_14a1_14ff[keysym - 0x14a1];
1306     else if (keysym > 0x15cf && keysym < 0x15f7)
1307         return keysym_to_unicode_15d0_15f6[keysym - 0x15d0];
1308     else if (keysym > 0x169f && keysym < 0x16f7)
1309         return keysym_to_unicode_16a0_16f6[keysym - 0x16a0];
1310     else if (keysym > 0x1e9e && keysym < 0x1f00)
1311         return keysym_to_unicode_1e9f_1eff[keysym - 0x1e9f];
1312     else if (keysym > 0x209f && keysym < 0x20ad)
1313         return keysym_to_unicode_20a0_20ac[keysym - 0x20a0];
1314     else
1315         return 0;
1316 }
1317 
1318 /*
1319  * Local variables:
1320  *  c-indent-level: 4
1321  *  c-basic-offset: 4
1322  * End:
1323  */
1324