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