1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2 
3 /*
4  * im_uim.c - uim plugin for mlterm
5  *
6  * Copyright (C) 2004 Seiichi SATO <ssato@sh.rim.or.jp>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
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. The name of any author may not be used to endorse or promote
17  *    products derived from this software without their specific prior
18  *    written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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  *	$Id$
34  */
35 
36 #include <stdio.h>
37 #include <uim.h>
38 #include <uim-helper.h>
39 #include <uim-im-switcher.h>
40 
41 #if UIM_VERSION_REQUIRE(1, 5, 0)
42 #include <uim-scm.h>
43 #else
44 #define uim_scm_is_initialized() (initialized)
45 #endif
46 
47 #include <pobl/bl_mem.h>    /* malloc/alloca/free */
48 #include <pobl/bl_str.h>    /* strdup/bl_str_sep/bl_snprintf */
49 #include <pobl/bl_locale.h> /* bl_get_locale */
50 #include <pobl/bl_slist.h>
51 
52 #include <ui_im.h>
53 
54 #include "../im_common.h"
55 #include "../im_info.h"
56 
57 #if 0
58 #define IM_UIM_DEBUG 1
59 #endif
60 
61 #if 0
62 /* Experimental (for twm, framebuffer etc where it's impossible to show status
63  * on system tray.) */
64 #define SHOW_STATUS_SCREEN
65 #endif
66 
67 /*
68  * When uim encoding is the same as terminal, parser_uim and conv are NULL,
69  * so encoding of received string will not be converted.
70  */
71 #define NEED_TO_CONV(uim) ((uim)->parser_uim && (uim)->conv)
72 
73 typedef struct im_uim {
74   /* input method common object */
75   ui_im_t im;
76 
77   uim_context context;
78 
79   vt_char_encoding_t term_encoding;
80 
81   char *encoding_name; /* encoding of conversion engine */
82 
83   /* parser_uim and conv are NULL if term_encoding == uim encoding  */
84   ef_parser_t *parser_uim;  /* for uim encoding  */
85   ef_parser_t *parser_term; /* for term encoding */
86   ef_conv_t *conv;          /* for term encoding */
87 
88   u_int pressing_mod_key;
89   u_int mod_ignore_mask;
90 
91   u_int cand_limit;
92 
93   int is_mozc;
94 
95 #ifdef SHOW_STATUS_SCREEN
96   int mode;
97 #endif
98 
99   struct im_uim *next;
100 
101 } im_uim_t;
102 
103 /* --- static variables --- */
104 
105 static im_uim_t *uim_list = NULL;
106 static int ref_count = 0;
107 static int initialized = 0;
108 static int helper_fd = -1;
109 static im_uim_t *focused_uim = NULL;
110 static ui_im_export_syms_t *syms = NULL; /* mlterm internal symbols */
111 static int mod_key_debug = 0;
112 
113 /* --- static functions --- */
114 
115 #ifdef SHOW_STATUS_SCREEN
update_stat_screen(im_uim_t * uim,int mode_changed)116 static void update_stat_screen(im_uim_t *uim, int mode_changed) {
117   if (!uim->im.cand_screen) {
118     if (uim->mode == 0 /* direct input */ /* || uim->im.preedit.filled_len == 0 */) {
119       if (uim->im.stat_screen) {
120         (*uim->im.stat_screen->destroy)(uim->im.stat_screen);
121         uim->im.stat_screen = NULL;
122       }
123 
124       return;
125     }
126 
127     if (!uim->im.stat_screen) {
128       int x;
129       int y;
130 
131       (*uim->im.listener->get_spot)(uim->im.listener->self, NULL, 0, &x, &y);
132 
133       if (!(uim->im.stat_screen = (*syms->ui_im_status_screen_new)(
134                 uim->im.disp, uim->im.font_man, uim->im.color_man, uim->im.vtparser,
135                 (*uim->im.listener->is_vertical)(uim->im.listener->self),
136                 (*uim->im.listener->get_line_height)(uim->im.listener->self), x, y))) {
137         return;
138       }
139     } else if (!mode_changed) {
140       return;
141     }
142 
143     (*uim->im.stat_screen->set)(uim->im.stat_screen, uim->parser_uim,
144                                 (u_char *)uim_get_mode_name(uim->context, uim->mode));
145   }
146 }
147 #else
148 #define update_stat_screen(uim, mode_changed) (0)
149 #endif
150 
find_engine(const char * engine,char ** encoding_name)151 static int find_engine(const char *engine, char **encoding_name) {
152   uim_context u;
153   int i;
154   int result;
155 
156   if (encoding_name == NULL) {
157     return 0;
158   }
159 
160   if (!(u = uim_create_context(NULL, "UTF-8", NULL, NULL, NULL, NULL))) {
161     return 0;
162   }
163   result = 0;
164 
165   for (i = 0; i < uim_get_nr_im(u); i++) {
166     if (strcmp(uim_get_im_name(u, i), engine) == 0) {
167       *encoding_name = (char *)uim_get_im_encoding(u, i);
168 
169       if (*encoding_name && (*encoding_name = strdup(*encoding_name))) {
170 #ifdef IM_UIM_DEBUG
171         bl_debug_printf(BL_DEBUG_TAG "conversion engine: %s, native encoding: %s\n", engine,
172                         *encoding_name);
173 #endif
174 
175         result = 1;
176 
177         break;
178       }
179     }
180   }
181 
182   uim_release_context(u);
183 
184   return result;
185 }
186 
xksym_to_ukey(KeySym ksym)187 static int xksym_to_ukey(KeySym ksym) {
188   /* Latin 1 */
189   if (0x20 <= ksym && ksym <= 0x7e) {
190     return ksym;
191   }
192 
193   switch (ksym) {
194     /* TTY Functions */
195     case XK_BackSpace:
196       return UKey_Backspace;
197     case XK_Tab:
198       return UKey_Tab;
199     case XK_Return:
200       return UKey_Return;
201     case XK_Escape:
202       return UKey_Escape;
203     case XK_Delete:
204       return UKey_Delete;
205 #ifdef XK_Multi_key
206     /* International & multi-key character composition */
207     case XK_Multi_key:
208       return UKey_Multi_key;
209 #endif
210     /* Japanese keyboard support */
211     case XK_Muhenkan:
212       return UKey_Muhenkan;
213     case XK_Henkan_Mode:
214       return UKey_Henkan_Mode;
215     case XK_Zenkaku_Hankaku:
216       return UKey_Zenkaku_Hankaku;
217     case XK_Hiragana_Katakana:
218       return UKey_Hiragana_Katakana;
219     /* Cursor control & motion */
220     case XK_Home:
221       return UKey_Home;
222     case XK_Left:
223       return UKey_Left;
224     case XK_Up:
225       return UKey_Up;
226     case XK_Right:
227       return UKey_Right;
228     case XK_Down:
229       return UKey_Down;
230     case XK_Prior:
231       return UKey_Prior;
232     case XK_Next:
233       return UKey_Next;
234     case XK_End:
235       return UKey_End;
236     case XK_F1:
237       return UKey_F1;
238     case XK_F2:
239       return UKey_F2;
240     case XK_F3:
241       return UKey_F3;
242     case XK_F4:
243       return UKey_F4;
244     case XK_F5:
245       return UKey_F5;
246     case XK_F6:
247       return UKey_F6;
248     case XK_F7:
249       return UKey_F7;
250     case XK_F8:
251       return UKey_F8;
252     case XK_F9:
253       return UKey_F9;
254     case XK_F10:
255       return UKey_F10;
256     case XK_F11:
257       return UKey_F11;
258     case XK_F12:
259       return UKey_F12;
260     case XK_F13:
261       return UKey_F13;
262     case XK_F14:
263       return UKey_F14;
264     case XK_F15:
265       return UKey_F15;
266     case XK_F16:
267       return UKey_F16;
268     case XK_F17:
269       return UKey_F17;
270     case XK_F18:
271       return UKey_F18;
272     case XK_F19:
273       return UKey_F19;
274     case XK_F20:
275       return UKey_F20;
276     case XK_F21:
277       return UKey_F21;
278     case XK_F22:
279       return UKey_F22;
280     case XK_F23:
281       return UKey_F23;
282     case XK_F24:
283       return UKey_F24;
284 #ifdef XK_F25
285     case XK_F25:
286       return UKey_F25;
287     case XK_F26:
288       return UKey_F26;
289     case XK_F27:
290       return UKey_F27;
291     case XK_F28:
292       return UKey_F28;
293     case XK_F29:
294       return UKey_F29;
295     case XK_F30:
296       return UKey_F30;
297     case XK_F31:
298       return UKey_F31;
299     case XK_F32:
300       return UKey_F32;
301     case XK_F33:
302       return UKey_F33;
303     case XK_F34:
304       return UKey_F34;
305     case XK_F35:
306       return UKey_F35;
307 #endif
308 #ifdef XK_KP_Space
309     case XK_KP_Space:
310       return ' ';
311 #endif
312 #ifdef XK_KP_Tab
313     case XK_KP_Tab:
314       return UKey_Tab;
315 #endif
316 #ifdef XK_KP_Enter
317     case XK_KP_Enter:
318       return UKey_Return;
319 #endif
320     case XK_KP_F1:
321       return UKey_F1;
322     case XK_KP_F2:
323       return UKey_F2;
324     case XK_KP_F3:
325       return UKey_F3;
326     case XK_KP_F4:
327       return UKey_F4;
328     case XK_KP_Home:
329       return UKey_Home;
330     case XK_KP_Left:
331       return UKey_Left;
332     case XK_KP_Up:
333       return UKey_Up;
334     case XK_KP_Right:
335       return UKey_Right;
336     case XK_KP_Down:
337       return UKey_Down;
338     case XK_KP_Prior:
339       return UKey_Prior;
340     case XK_KP_Next:
341       return UKey_Next;
342     case XK_KP_End:
343       return UKey_End;
344     case XK_KP_Delete:
345       return UKey_Delete;
346 #ifdef XK_KP_Equal
347     case XK_KP_Equal:
348       return '=';
349 #endif
350     case XK_KP_Multiply:
351       return '*';
352     case XK_KP_Add:
353       return '+';
354     case XK_KP_Separator:
355       return ',';
356     case XK_KP_Subtract:
357       return '-';
358     case XK_KP_Decimal:
359       return '.';
360     case XK_KP_Divide:
361       return '/';
362     /* keypad numbers */
363     case XK_KP_0:
364       return '0';
365     case XK_KP_1:
366       return '1';
367     case XK_KP_2:
368       return '2';
369     case XK_KP_3:
370       return '3';
371     case XK_KP_4:
372       return '4';
373     case XK_KP_5:
374       return '5';
375     case XK_KP_6:
376       return '6';
377     case XK_KP_7:
378       return '7';
379     case XK_KP_8:
380       return '8';
381     case XK_KP_9:
382       return '9';
383     default:
384       return UKey_Other;
385   }
386 }
387 
helper_disconnected(void)388 static void helper_disconnected(void) {
389   (*syms->ui_event_source_remove_fd)(helper_fd);
390 
391   helper_fd = -1;
392 }
393 
prop_list_update(void * p,const char * str)394 static void prop_list_update(void *p, const char *str) {
395   im_uim_t *uim = NULL;
396   char buf[BUFSIZ];
397   int len;
398 
399 #ifdef IM_UIM_DEBUG
400   bl_debug_printf(BL_DEBUG_TAG " str: %s\n", str);
401 #endif
402 
403   uim = (im_uim_t *)p;
404 
405   if (focused_uim != uim) {
406     return;
407   }
408 
409 #define PROP_LIST_FORMAT "prop_list_update\ncharset=%s\n%s"
410 
411   len = strlen(PROP_LIST_FORMAT) + strlen(uim->encoding_name) + strlen(str) + 1;
412 
413   if (len > sizeof(buf)) {
414 #ifdef DEBUG
415     bl_warn_printf(BL_DEBUG_TAG " property list string is too long.");
416 #endif
417     return;
418   }
419 
420   bl_snprintf(buf, sizeof(buf), PROP_LIST_FORMAT, uim->encoding_name, str);
421 
422   uim_helper_send_message(helper_fd, buf);
423 
424 #undef PROP_LIST_FORMAT
425 }
426 
prop_label_update(void * p,const char * str)427 static void prop_label_update(void *p, const char *str) {
428   im_uim_t *uim = NULL;
429   char buf[BUFSIZ];
430   int len;
431 
432 #ifdef IM_UIM_DEBUG
433   bl_debug_printf(BL_DEBUG_TAG " prop_label_update(), str: %s\n", str);
434 #endif
435 
436   uim = (im_uim_t *)p;
437 
438   if (focused_uim != uim) {
439     return;
440   }
441 
442 #define PROP_LABEL_FORMAT "prop_label_update\ncharset=%s\n%s"
443 
444   len = strlen(PROP_LABEL_FORMAT) + strlen(uim->encoding_name) + strlen(str) + 1;
445 
446   if (len > sizeof(buf)) {
447 #ifdef DEBUG
448     bl_warn_printf(BL_DEBUG_TAG " property label string is too long.");
449 #endif
450 
451     return;
452   }
453 
454   bl_snprintf(buf, sizeof(buf), PROP_LABEL_FORMAT, uim->encoding_name, str);
455 
456   uim_helper_send_message(helper_fd, buf);
457 
458 #undef PROP_LABEL_FORMAT
459 }
460 
461 #ifdef SHOW_STATUS_SCREEN
mode_update(void * p,int mode)462 static void mode_update(void *p, int mode) {
463   im_uim_t *uim = NULL;
464 
465   uim = (im_uim_t *)p;
466 
467   uim->mode = mode;
468 
469   update_stat_screen(uim, 1);
470 }
471 #endif
472 
commit(void * p,const char * str)473 static void commit(void *p, const char *str) {
474   im_uim_t *uim;
475 
476 #ifdef IM_UIM_DEBUG
477   bl_debug_printf(BL_DEBUG_TAG "str: %s\n", str);
478 #endif
479 
480   uim = (im_uim_t *)p;
481 
482   (*uim->im.listener->write_to_term)(uim->im.listener->self, str, strlen(str),
483                                      uim->parser_uim);
484 }
485 
preedit_clear(void * ptr)486 static void preedit_clear(void *ptr) {
487   im_uim_t *uim;
488 
489 #ifdef IM_UIM_DEBUG
490   bl_debug_printf(BL_DEBUG_TAG "\n");
491 #endif
492 
493   uim = (im_uim_t *)ptr;
494 
495   if (uim->im.preedit.chars) {
496     (*syms->vt_str_destroy)(uim->im.preedit.chars, uim->im.preedit.num_chars);
497     uim->im.preedit.chars = NULL;
498   }
499 
500   uim->im.preedit.num_chars = 0;
501   uim->im.preedit.filled_len = 0;
502   uim->im.preedit.segment_offset = 0;
503   uim->im.preedit.cursor_offset = UI_IM_PREEDIT_NOCURSOR;
504 
505   update_stat_screen(uim, 0);
506 }
507 
preedit_pushback(void * ptr,int attr,const char * _str)508 static void preedit_pushback(void *ptr, int attr, const char *_str) {
509   im_uim_t *uim;
510   u_char *str;
511   vt_char_t *p;
512   ef_char_t ch;
513   vt_color_t fg_color = VT_FG_COLOR;
514   vt_color_t bg_color = VT_BG_COLOR;
515   int is_underline = 0;
516   u_int count = 0;
517   size_t len;
518 
519 #ifdef IM_UIM_DEBUG
520   bl_debug_printf(BL_DEBUG_TAG " attr: %d, _str:%s, length:%d\n", attr, _str, strlen(_str));
521 #endif
522 
523   uim = (im_uim_t *)ptr;
524 
525   if (attr & UPreeditAttr_Cursor) {
526     uim->im.preedit.cursor_offset = uim->im.preedit.filled_len;
527   }
528 
529   if (!(len = strlen(_str))) {
530     return;
531   }
532 
533   if (attr & UPreeditAttr_Reverse) {
534     uim->im.preedit.segment_offset = uim->im.preedit.filled_len;
535     uim->im.preedit.cursor_offset = UI_IM_PREEDIT_NOCURSOR;
536     fg_color = VT_BG_COLOR;
537     bg_color = VT_FG_COLOR;
538   }
539 
540   if (attr & UPreeditAttr_UnderLine) {
541     is_underline = 1;
542   }
543 
544   /* TODO: UPreeditAttr_Separator */
545 
546   if (NEED_TO_CONV(uim)) {
547     /* uim encoding -> term encoding */
548     if (!im_convert_encoding(uim->parser_uim, uim->conv, (u_char *)_str, &str, len + 1)) {
549       return;
550     }
551   } else {
552     str = (u_char *)_str;
553   }
554 
555   len = strlen(str);
556 
557   /*
558    * count number of characters to re-allocate im.preedit.chars
559    */
560 
561   (*uim->parser_term->init)(uim->parser_term);
562   (*uim->parser_term->set_str)(uim->parser_term, (u_char *)str, len);
563 
564   while ((*uim->parser_term->next_char)(uim->parser_term, &ch)) {
565     count++;
566   }
567 
568   /* no space left? (current array size < new array size?)*/
569   if (uim->im.preedit.num_chars < uim->im.preedit.filled_len + count) {
570     if (!(p = realloc(uim->im.preedit.chars,
571                       sizeof(vt_char_t) * (uim->im.preedit.filled_len + count)))) {
572 #ifdef DEBUG
573       bl_warn_printf(BL_DEBUG_TAG " realloc failed.\n");
574 #endif
575 
576       (*syms->vt_str_destroy)(uim->im.preedit.chars, uim->im.preedit.num_chars);
577       uim->im.preedit.chars = NULL;
578       uim->im.preedit.num_chars = 0;
579       uim->im.preedit.filled_len = 0;
580 
581       if (NEED_TO_CONV(uim)) {
582         free(str);
583       }
584 
585       return;
586     }
587 
588     uim->im.preedit.chars = p;
589     uim->im.preedit.num_chars = uim->im.preedit.filled_len + count;
590   }
591 
592   /*
593    * u_char --> vt_char_t
594    */
595 
596   p = &uim->im.preedit.chars[uim->im.preedit.filled_len];
597   (*syms->vt_str_init)(p, count);
598 
599   (*uim->parser_term->init)(uim->parser_term);
600   (*uim->parser_term->set_str)(uim->parser_term, (u_char *)str, len);
601 
602   while ((*uim->parser_term->next_char)(uim->parser_term, &ch)) {
603     int is_fullwidth = 0;
604     int is_comb = 0;
605 
606     if ((*syms->vt_convert_to_internal_ch)(uim->im.vtparser, &ch) <= 0) {
607       continue;
608     }
609 
610     if (ch.property & EF_FULLWIDTH) {
611       is_fullwidth = 1;
612     } else if (ch.property & EF_AWIDTH) {
613       /* TODO: check col_size_of_width_a */
614       is_fullwidth = 1;
615     }
616 
617     if (ch.property & EF_COMBINING) {
618       is_comb = 1;
619 
620       if ((*syms->vt_char_combine)(p - 1, ef_char_to_int(&ch), ch.cs, is_fullwidth,
621                                    (ch.property & EF_AWIDTH) ? 1 : 0, is_comb,
622                                    fg_color, bg_color, 0, 0, is_underline, 0, 0)) {
623         continue;
624       }
625 
626       /*
627        * if combining failed , char is normally appended.
628        */
629     }
630 
631     (*syms->vt_char_set)(p, ef_char_to_int(&ch), ch.cs, is_fullwidth,
632                          (ch.property & EF_AWIDTH) ? 1 : 0, is_comb, fg_color, bg_color,
633                          0, 0, is_underline, 0, 0);
634 
635     p++;
636     uim->im.preedit.filled_len++;
637   }
638 
639   if (NEED_TO_CONV(uim)) {
640     free(str);
641   }
642 }
643 
preedit_update(void * ptr)644 static void preedit_update(void *ptr) {
645   im_uim_t *uim;
646 
647   uim = (im_uim_t *)ptr;
648 
649   (*uim->im.listener->draw_preedit_str)(uim->im.listener->self, uim->im.preedit.chars,
650                                         uim->im.preedit.filled_len, uim->im.preedit.cursor_offset);
651   update_stat_screen(uim, 0);
652 }
653 
654 /*
655  * callback for candidate screen events
656  */
657 
candidate_selected(void * p,u_int index)658 static void candidate_selected(void *p, u_int index) {
659 #ifdef IM_UIM_DEBUG
660   bl_debug_printf(BL_DEBUG_TAG " index : %d\n", index);
661 #endif
662   uim_set_candidate_index(((im_uim_t *)p)->context, index);
663 }
664 
665 /*
666  * callbacks for candidate selector
667  */
668 
candidate_activate(void * p,int num,int limit)669 static void candidate_activate(void *p, int num, int limit) {
670   im_uim_t *uim;
671   int x;
672   int y;
673   int i;
674 
675 #ifdef IM_UIM_DEBUG
676   bl_debug_printf(BL_DEBUG_TAG " num: %d limit: %d\n", num, limit);
677 #endif
678 
679   uim = (im_uim_t *)p;
680 
681   (*uim->im.listener->get_spot)(uim->im.listener->self, uim->im.preedit.chars,
682                                 uim->im.preedit.segment_offset, &x, &y);
683 
684   if (uim->im.stat_screen) {
685     (*uim->im.stat_screen->destroy)(uim->im.stat_screen);
686     uim->im.stat_screen = NULL;
687   }
688 
689   if (uim->im.cand_screen == NULL) {
690     if (!(uim->im.cand_screen = (*syms->ui_im_candidate_screen_new)(
691               uim->im.disp, uim->im.font_man, uim->im.color_man, uim->im.vtparser,
692               (*uim->im.listener->is_vertical)(uim->im.listener->self), 1,
693               (*uim->im.listener->get_line_height)(uim->im.listener->self), x, y))) {
694 #ifdef DEBUG
695       bl_warn_printf(BL_DEBUG_TAG " ui_im_candidate_screen_new() failed.\n");
696 #endif
697 
698       return;
699     }
700 
701     uim->im.cand_screen->listener.self = uim;
702     uim->im.cand_screen->listener.selected = candidate_selected;
703   }
704 
705   if (!(*uim->im.cand_screen->init)(uim->im.cand_screen, num, limit)) {
706     (*uim->im.cand_screen->destroy)(uim->im.cand_screen);
707     uim->im.cand_screen = NULL;
708 
709     return;
710   }
711 
712   (*uim->im.cand_screen->set_spot)(uim->im.cand_screen, x, y);
713 
714   for (i = 0; i < num; i++) {
715     uim_candidate c;
716     u_char *_p;
717     u_char *p = NULL;
718     const char *heading;
719     u_int info;
720 
721     c = uim_get_candidate(uim->context, i, i);
722     _p = (u_char *)uim_candidate_get_cand_str(c);
723     heading = uim_candidate_get_heading_label(c);
724     if (heading && heading[0]) {
725       /* heading[1] may be '\0' */
726       info = (((u_int)heading[0]) << 16) | (((u_int)heading[1]) << 24) | i;
727     } else {
728       info = (((u_int)' ') << 16) | i;
729     }
730 
731 #ifdef IM_UIM_DEBUG
732     bl_debug_printf(BL_DEBUG_TAG " %d%s%s%s| %s\n", i, (heading && heading[0]) ? "(" : "",
733                     (heading && heading[0]) ? heading : "", (heading && heading[0]) ? ")" : "", _p);
734 #endif
735 
736     if (NEED_TO_CONV(uim)) {
737       (*uim->parser_uim->init)(uim->parser_uim);
738       if (im_convert_encoding(uim->parser_uim, uim->conv, _p, &p, strlen(_p) + 1)) {
739         (*uim->im.cand_screen->set)(uim->im.cand_screen, uim->parser_term, p, info);
740         free(p);
741       }
742     } else {
743       (*uim->im.cand_screen->set)(uim->im.cand_screen, uim->parser_term, _p, i);
744     }
745 
746     uim_candidate_free(c);
747   }
748 
749   (*uim->im.cand_screen->select)(uim->im.cand_screen, 0);
750   uim->cand_limit = limit;
751 }
752 
candidate_select(void * p,int index)753 static void candidate_select(void *p, int index) {
754   im_uim_t *uim;
755 
756 #ifdef IM_UIM_DEBUG
757   bl_debug_printf(BL_DEBUG_TAG " index: %d\n", index);
758 #endif
759 
760   uim = (im_uim_t *)p;
761 
762   if (uim->im.cand_screen) {
763     /*
764      * XXX Hack for uim-mozc (1.11.1522.102)
765      * If candidate_activate() is called with num == 20 and limit = 10,
766      * uim_get_candidate() on mozc doesn't returns 20 candidates but 10 ones.
767      * (e.g. uim_get_candidate(0) and uim_get_candidate(10) returns the same.)
768      */
769     if (uim->is_mozc && uim->im.cand_screen->index != index &&
770         uim->im.cand_screen->index / uim->cand_limit != index / uim->cand_limit) {
771       int mod = index % uim->cand_limit;
772 
773       if (mod == 0 || mod == uim->cand_limit - 1) {
774         candidate_activate(p, uim->im.cand_screen->num_candidates, uim->cand_limit);
775       }
776     }
777 
778     (*uim->im.cand_screen->select)(uim->im.cand_screen, index);
779   }
780 }
781 
candidate_shift_page(void * p,int direction)782 static void candidate_shift_page(void *p, int direction) {
783   im_uim_t *uim;
784   int index;
785 
786 #ifdef IM_UIM_DEBUG
787   bl_debug_printf(BL_DEBUG_TAG " direction: %s\n", direction ? "next" : "prev");
788 #endif
789 
790   uim = (im_uim_t *)p;
791 
792   if (!uim->im.cand_screen) {
793     return;
794   }
795 
796   index = (int)uim->im.cand_screen->index;
797 
798   if (!direction && index < uim->cand_limit) {
799     /* top page -> last page */
800     index = (uim->im.cand_screen->num_candidates / uim->cand_limit) * uim->cand_limit + index;
801   } else if (direction &&
802              ((index / uim->cand_limit) + 1) * uim->cand_limit >
803                  uim->im.cand_screen->num_candidates) {
804     /* last page -> top page */
805     index = index % uim->cand_limit;
806   } else {
807     /* shift page according to the direction */
808     index += (direction ? uim->cand_limit : -(uim->cand_limit));
809   }
810 
811   if (index < 0) {
812     index = 0;
813   } else if (index >= uim->im.cand_screen->num_candidates) {
814     index = uim->im.cand_screen->num_candidates - 1;
815   }
816 
817   (*uim->im.cand_screen->select)(uim->im.cand_screen, index);
818   uim_set_candidate_index(uim->context, index);
819 }
820 
candidate_deactivate(void * p)821 static void candidate_deactivate(void *p) {
822   im_uim_t *uim;
823 
824 #ifdef IM_UIM_DEBUG
825   bl_debug_printf(BL_DEBUG_TAG "\n");
826 #endif
827 
828   uim = (im_uim_t *)p;
829 
830   if (uim->im.cand_screen) {
831     (*uim->im.cand_screen->destroy)(uim->im.cand_screen);
832     uim->im.cand_screen = NULL;
833   }
834 }
835 
836 /*
837  * methods of ui_im_t
838  */
839 
destroy(ui_im_t * im)840 static void destroy(ui_im_t *im) {
841   im_uim_t *uim;
842 
843   uim = (im_uim_t *)im;
844 
845   if (focused_uim == uim) {
846     focused_uim = NULL;
847   }
848 
849   if (uim->parser_uim) {
850     (*uim->parser_uim->destroy)(uim->parser_uim);
851   }
852 
853   (*uim->parser_term->destroy)(uim->parser_term);
854 
855   if (uim->conv) {
856     (*uim->conv->destroy)(uim->conv);
857   }
858 
859   uim_release_context(uim->context);
860 
861   ref_count--;
862 
863 #ifdef IM_UIM_DEBUG
864   bl_debug_printf(BL_DEBUG_TAG " An object was destroyed. ref_count: %d\n", ref_count);
865 #endif
866 
867   bl_slist_remove(uim_list, uim);
868 
869   free(uim->encoding_name);
870   free(uim);
871 
872   if (ref_count == 0 && initialized) {
873     (*syms->ui_event_source_remove_fd)(helper_fd);
874     uim_helper_close_client_fd(helper_fd);
875     helper_fd = -1;
876 
877     if (initialized > 0) {
878       uim_quit();
879       initialized = 0;
880     }
881   }
882 }
883 
key_event(ui_im_t * im,u_char key_char,KeySym ksym,XKeyEvent * event)884 static int key_event(ui_im_t *im, u_char key_char, KeySym ksym, XKeyEvent *event) {
885   im_uim_t *uim;
886   int key = 0;
887   int state = 0;
888   int ret;
889 
890   int is_shift;
891   int is_lock;
892   int is_ctl;
893   int is_alt;
894   int is_meta;
895   int is_super;
896   int is_hyper;
897 
898   uim = (im_uim_t *)im;
899 
900   if (mod_key_debug) {
901     bl_msg_printf(">>--------------------------------\n");
902     bl_msg_printf(">>event->state    : %.8x\n", event->state);
903     bl_msg_printf(">>mod_ignore_mask : %.8x\n", uim->mod_ignore_mask);
904     bl_msg_printf(">>ksym            : %.8x\n", ksym);
905   }
906 
907 #ifdef USE_XLIB
908   if (!(event->state & uim->mod_ignore_mask)) {
909     uim->pressing_mod_key = 0;
910   }
911 
912   switch (ksym) {
913     case XK_Shift_L:
914     case XK_Shift_R:
915       uim->pressing_mod_key |= UMod_Shift;
916       break;
917     case XK_Control_L:
918     case XK_Control_R:
919       uim->pressing_mod_key |= UMod_Control;
920       break;
921     case XK_Alt_L:
922     case XK_Alt_R:
923       uim->pressing_mod_key |= UMod_Alt;
924       break;
925     case XK_Meta_L:
926     case XK_Meta_R:
927       uim->pressing_mod_key |= UMod_Meta;
928       break;
929     case XK_Super_L:
930     case XK_Super_R:
931       uim->pressing_mod_key |= UMod_Super;
932       break;
933     case XK_Hyper_L:
934     case XK_Hyper_R:
935       uim->pressing_mod_key |= UMod_Hyper;
936       break;
937     default:
938       break;
939   }
940 #else
941   uim->pressing_mod_key = ~0;
942 #endif
943 
944   (*uim->im.listener->compare_key_state_with_modmap)(uim->im.listener->self, event->state,
945                                                      &is_shift, &is_lock, &is_ctl, &is_alt,
946                                                      &is_meta, NULL, &is_super, &is_hyper);
947 
948   if (is_shift && (uim->pressing_mod_key & UMod_Shift)) {
949     state |= UMod_Shift;
950   }
951   if (is_ctl && (uim->pressing_mod_key & UMod_Control)) {
952     state |= UMod_Control;
953   }
954   if (is_alt && (uim->pressing_mod_key & UMod_Alt)) {
955     state |= UMod_Alt;
956   }
957   if (is_meta && (uim->pressing_mod_key & UMod_Meta)) {
958     state |= UMod_Meta;
959   }
960   if (is_super && (uim->pressing_mod_key & UMod_Super)) {
961     state |= UMod_Super;
962   }
963   if (is_hyper && (uim->pressing_mod_key & UMod_Hyper)) {
964     state |= UMod_Hyper;
965   }
966 
967   if (mod_key_debug) {
968     bl_msg_printf(">>pressing_mod_key: %.8x\n", uim->pressing_mod_key);
969     bl_msg_printf(">>state           : %.8x\n", state);
970     bl_msg_printf(">>--------------------------------\n");
971   }
972 
973   key = xksym_to_ukey(ksym);
974 
975   ret = uim_press_key(uim->context, key, state);
976   uim_release_key(uim->context, key, state);
977 
978   return ret;
979 }
980 
switch_mode(ui_im_t * im)981 static int switch_mode(ui_im_t *im) { return 0; }
982 
is_active(ui_im_t * im)983 static int is_active(ui_im_t *im) {
984 #ifdef SHOW_STATUS_SCREEN
985   if (((im_uim_t *)im)->mode > 0) {
986     return 1;
987   } else
988 #endif
989   {
990     return 0;
991   }
992 }
993 
focused(ui_im_t * im)994 static void focused(ui_im_t *im) {
995   im_uim_t *uim;
996 
997   uim = (im_uim_t *)im;
998 
999   uim_helper_client_focus_in(uim->context);
1000 
1001   focused_uim = uim;
1002 
1003   uim_prop_list_update(uim->context);
1004   uim_prop_label_update(uim->context);
1005 
1006   if (uim->im.cand_screen) {
1007     (*uim->im.cand_screen->show)(uim->im.cand_screen);
1008   }
1009 
1010   update_stat_screen(uim, 0);
1011 
1012   uim->pressing_mod_key = 0;
1013 }
1014 
unfocused(ui_im_t * im)1015 static void unfocused(ui_im_t *im) {
1016   im_uim_t *uim;
1017 
1018   uim = (im_uim_t *)im;
1019 
1020   uim_helper_client_focus_out(uim->context);
1021 
1022   if (uim->im.cand_screen) {
1023     (*uim->im.cand_screen->hide)(uim->im.cand_screen);
1024   }
1025 
1026 #ifdef SHOW_STATUS_SCREEN
1027   if (uim->im.stat_screen) {
1028     (*uim->im.stat_screen->hide)(uim->im.stat_screen);
1029   }
1030 #endif
1031 
1032   uim->pressing_mod_key = 0;
1033 }
1034 
1035 /*
1036  * helper
1037  */
1038 
helper_send_imlist(void)1039 static void helper_send_imlist(void) {
1040   const char *selected_name;
1041   const char *name;
1042   const char *lang;
1043   const char *dsc;
1044   char *buf = NULL;
1045   int i;
1046   u_int len = 0;
1047   u_int filled_len = 0;
1048 
1049   if (!focused_uim) {
1050     return;
1051   }
1052 
1053 #define HEADER_FORMAT "im_list\ncharset=%s\n"
1054 
1055   len += strlen(HEADER_FORMAT) + strlen(focused_uim->encoding_name);
1056 
1057   selected_name = uim_get_current_im_name(focused_uim->context);
1058   len += strlen(selected_name);
1059   len += strlen("selected");
1060 
1061   for (i = 0; i < uim_get_nr_im(focused_uim->context); i++) {
1062     name = uim_get_im_name(focused_uim->context, i);
1063     lang = uim_get_im_language(focused_uim->context, i);
1064     dsc = uim_get_im_short_desc(focused_uim->context, i);
1065 
1066     len += name ? strlen(name) : 0;
1067     len += lang ? strlen(lang) : 0;
1068     len += dsc ? strlen(dsc) : 0;
1069     len += strlen("\t\t\t\n");
1070   }
1071 
1072   len++;
1073 
1074   if (!(buf = alloca(sizeof(char) * len))) {
1075 #ifdef DEBUG
1076     bl_warn_printf(BL_DEBUG_TAG " alloca failed\n");
1077 #endif
1078     return;
1079   }
1080 
1081   filled_len = bl_snprintf(buf, len, HEADER_FORMAT, focused_uim->encoding_name);
1082 
1083 #undef HEADER_FORMAT
1084 
1085   for (i = 0; i < uim_get_nr_im(focused_uim->context); i++) {
1086     name = uim_get_im_name(focused_uim->context, i);
1087     lang = uim_get_im_language(focused_uim->context, i);
1088     dsc = uim_get_im_short_desc(focused_uim->context, i);
1089 
1090     filled_len += bl_snprintf(&buf[filled_len], len - filled_len, "%s\t%s\t%s\t%s\n",
1091                               name ? name : "", lang ? lang : "", dsc ? dsc : "",
1092                               strcmp(name, selected_name) == 0 ? "selected" : "");
1093   }
1094 
1095 #ifdef IM_UIM_DEBUG
1096   bl_debug_printf("----\n%s----\n", buf);
1097 #endif
1098 
1099   uim_helper_send_message(helper_fd, buf);
1100 }
1101 
helper_im_changed(char * request,char * engine_name)1102 static void helper_im_changed(char *request, char *engine_name) {
1103   char *buf;
1104   size_t len;
1105 
1106   len = strlen(engine_name) + 5;
1107 
1108   if (!(buf = alloca(len))) {
1109 #ifdef DEBUG
1110     bl_warn_printf(BL_DEBUG_TAG " alloca failed\n");
1111 #endif
1112     return;
1113   }
1114 
1115   bl_snprintf(buf, len, "uim:%s", engine_name);
1116 
1117   /*
1118    * we don't use uim_change_input_method_engine(), since it cannot
1119    * specify encoding.
1120    */
1121 
1122   if (strcmp(request, "im_change_this_text_area_only") == 0) {
1123     if (focused_uim) {
1124       (*focused_uim->im.listener->im_changed)(focused_uim->im.listener->self, buf);
1125     }
1126   } else if (strcmp(request, "im_change_whole_desktop") == 0 ||
1127              strcmp(request, "im_change_this_application_only") == 0) {
1128     im_uim_t *uim;
1129 
1130     uim = uim_list;
1131     while (uim) {
1132       (*uim->im.listener->im_changed)(uim->im.listener->self, buf);
1133       uim = bl_slist_next(uim);
1134     }
1135   }
1136 }
1137 
helper_update_custom(char * custom,char * value)1138 static void helper_update_custom(char *custom, char *value) {
1139   im_uim_t *uim;
1140 
1141   uim = uim_list;
1142   while (uim) {
1143     uim_prop_update_custom(uim->context, custom, value);
1144     uim = bl_slist_next(uim);
1145   }
1146 }
1147 
helper_commit_string(u_char * str)1148 static void helper_commit_string(u_char *str /* UTF-8? */) {
1149   ef_parser_t *parser_utf8;
1150 
1151   if (!focused_uim) {
1152     return;
1153   }
1154 
1155   if (focused_uim->term_encoding == VT_UTF8) {
1156     (*focused_uim->im.listener->write_to_term)(focused_uim->im.listener->self, str, strlen(str),
1157                                                NULL);
1158     return;
1159   }
1160 
1161   if (!(parser_utf8 = (*syms->vt_char_encoding_parser_new)(VT_UTF8))) {
1162     return;
1163   }
1164   (*focused_uim->im.listener->write_to_term)(focused_uim->im.listener->self, str, strlen(str),
1165                                              parser_utf8);
1166   (*parser_utf8->destroy)(parser_utf8);
1167 }
1168 
helper_read_handler(void)1169 static void helper_read_handler(void) {
1170   char *message;
1171 
1172   uim_helper_read_proc(helper_fd);
1173 
1174   while ((message = uim_helper_get_message())) {
1175     char *first_line;
1176     char *second_line;
1177 
1178 #ifdef IM_UIM_DEBUG
1179     bl_debug_printf("message recieved from helper: %s\n", message);
1180 #endif
1181 
1182     if ((first_line = bl_str_sep(&message, "\n"))) {
1183       if (strcmp(first_line, "prop_activate") == 0) {
1184         second_line = bl_str_sep(&message, "\n");
1185         if (second_line && focused_uim) {
1186           uim_prop_activate(focused_uim->context, second_line);
1187         }
1188       } else if (strcmp(first_line, "im_list_get") == 0) {
1189         helper_send_imlist();
1190       } else if (strncmp(first_line, "im_change_", 10) == 0) {
1191         if ((second_line = bl_str_sep(&message, "\n"))) {
1192           helper_im_changed(first_line, second_line);
1193         }
1194       } else if (strcmp(first_line, "prop_update_custom") == 0) {
1195         if ((second_line = bl_str_sep(&message, "\n"))) {
1196           helper_update_custom(second_line, message);
1197         }
1198       } else if (strcmp(first_line, "focus_in") == 0) {
1199         focused_uim = NULL;
1200       } else if (strcmp(first_line, "commit_string") == 0) {
1201         if ((second_line = bl_str_sep(&message, "\n"))) {
1202           helper_commit_string(second_line);
1203         }
1204       }
1205 
1206       message = first_line; /* for free() */
1207     }
1208 
1209     free(message);
1210   }
1211 }
1212 
1213 /* --- global functions --- */
1214 
im_uim_new(u_int64_t magic,vt_char_encoding_t term_encoding,ui_im_export_syms_t * export_syms,char * engine,u_int mod_ignore_mask)1215 ui_im_t *im_uim_new(u_int64_t magic, vt_char_encoding_t term_encoding,
1216                     ui_im_export_syms_t *export_syms, char *engine, u_int mod_ignore_mask) {
1217   im_uim_t *uim;
1218   char *encoding_name;
1219   vt_char_encoding_t encoding;
1220 
1221   if (magic != (u_int64_t)IM_API_COMPAT_CHECK_MAGIC) {
1222     bl_error_printf("Incompatible input method API.\n");
1223 
1224     return NULL;
1225   }
1226 
1227   uim = NULL;
1228   encoding_name = NULL;
1229 
1230   if (getenv("MOD_KEY_DEBUG")) {
1231     mod_key_debug = 1;
1232   }
1233 
1234 #if 1
1235 #define RESTORE_LOCALE
1236 #endif
1237 
1238   if (!uim_scm_is_initialized()) {
1239 #ifdef RESTORE_LOCALE
1240     /*
1241      * Workaround against make_locale() of m17nlib.
1242      */
1243     char *cur_locale;
1244 
1245     if ((cur_locale = alloca(strlen(bl_get_locale()) + 1))) {
1246       strcpy(cur_locale, bl_get_locale());
1247     }
1248 #endif
1249 
1250     if (uim_init() == -1) {
1251 #ifdef DEBUG
1252       bl_warn_printf(BL_DEBUG_TAG " failed to initialize uim.");
1253 #endif
1254 
1255       return NULL;
1256     }
1257 
1258 #ifdef RESTORE_LOCALE
1259     /* restoring */
1260     /*
1261      * TODO: remove valgrind warning.
1262      * The memory space pointed to by sys_locale in bl_locale.c
1263      * was freed by setlocale() in m17nlib.
1264      */
1265     bl_locale_init(cur_locale);
1266 #endif
1267 
1268     syms = export_syms;
1269 
1270     initialized = 1;
1271   } else if (!initialized) {
1272     initialized = -1;
1273   }
1274 
1275   /*
1276    * create I/O chanel for uim_helper_server
1277    */
1278   if (helper_fd == -1 && syms && syms->ui_event_source_add_fd && syms->ui_event_source_remove_fd) {
1279     helper_fd = uim_helper_init_client_fd(helper_disconnected);
1280 
1281     (*syms->ui_event_source_add_fd)(helper_fd, helper_read_handler);
1282   }
1283 
1284   if ((engine == NULL) || (strlen(engine) == 0)) {
1285     /*
1286      * The returned string's storage is invalidated when we
1287      * call uim next, so we need to make a copy.
1288      */
1289     char *p = (char *)uim_get_default_im_name(bl_get_locale());
1290 
1291     if ((engine = alloca(strlen(p) + 1))) {
1292       strcpy(engine, p);
1293     }
1294   }
1295 
1296   if (!find_engine(engine, &encoding_name)) {
1297     bl_error_printf("%s: No such conversion engine.\n", engine);
1298     goto error;
1299   }
1300 
1301   if ((encoding = (*syms->vt_get_char_encoding)(encoding_name)) == VT_UNKNOWN_ENCODING) {
1302 #ifdef DEBUG
1303     bl_warn_printf(BL_DEBUG_TAG " %s is unknown encoding.\n", encoding_name);
1304 #endif
1305     goto error;
1306   }
1307 
1308   if (!(uim = calloc(1, sizeof(im_uim_t)))) {
1309 #ifdef DEBUG
1310     bl_warn_printf(BL_DEBUG_TAG " malloc failed.\n");
1311 #endif
1312 
1313     goto error;
1314   }
1315 
1316   uim->term_encoding = term_encoding;
1317   uim->encoding_name = encoding_name;
1318   uim->mod_ignore_mask = mod_ignore_mask;
1319 
1320   if (uim->term_encoding != encoding) {
1321     if (!(uim->parser_uim = (*syms->vt_char_encoding_parser_new)(encoding))) {
1322       goto error;
1323     }
1324 
1325     if (!(uim->conv = (*syms->vt_char_encoding_conv_new)(term_encoding))) {
1326       goto error;
1327     }
1328   }
1329 
1330   if (!(uim->parser_term = (*syms->vt_char_encoding_parser_new)(term_encoding))) {
1331     goto error;
1332   }
1333 
1334   if (!(uim->context = uim_create_context(uim, encoding_name, NULL, engine, NULL, commit))) {
1335 #ifdef DEBUG
1336     bl_warn_printf(BL_DEBUG_TAG " could not create uim context.\n");
1337 #endif
1338 
1339     goto error;
1340   }
1341 
1342   uim->is_mozc = (strcmp(engine, "mozc") == 0);
1343 
1344   uim_set_preedit_cb(uim->context, preedit_clear, preedit_pushback, preedit_update);
1345 
1346   uim_set_candidate_selector_cb(uim->context, candidate_activate, candidate_select,
1347                                 candidate_shift_page, candidate_deactivate);
1348 
1349   uim_set_prop_list_update_cb(uim->context, prop_list_update);
1350   uim_set_prop_label_update_cb(uim->context, prop_label_update);
1351 
1352 #ifdef SHOW_STATUS_SCREEN
1353   uim_set_mode_cb(uim->context, mode_update);
1354 #endif
1355 
1356   focused_uim = uim;
1357   uim_prop_list_update(uim->context);
1358 
1359   /*
1360    * set methods of ui_im_t
1361    */
1362   uim->im.destroy = destroy;
1363   uim->im.key_event = key_event;
1364   uim->im.switch_mode = switch_mode;
1365   uim->im.is_active = is_active;
1366   uim->im.focused = focused;
1367   uim->im.unfocused = unfocused;
1368 
1369   bl_slist_insert_head(uim_list, uim);
1370 
1371   ref_count++;
1372 
1373 #ifdef IM_UIM_DEBUG
1374   bl_debug_printf("New object was created. ref_count is %d.\n", ref_count);
1375 #endif
1376 
1377   return (ui_im_t *)uim;
1378 
1379 error:
1380   if (helper_fd != -1) {
1381     (*syms->ui_event_source_remove_fd)(helper_fd);
1382 
1383     uim_helper_close_client_fd(helper_fd);
1384 
1385     helper_fd = -1;
1386   }
1387 
1388   if (initialized > 0 && ref_count == 0) {
1389     uim_quit();
1390     initialized = 0;
1391   }
1392 
1393   free(encoding_name);
1394 
1395   if (uim) {
1396     if (uim->parser_uim) {
1397       (*uim->parser_uim->destroy)(uim->parser_uim);
1398     }
1399 
1400     if (uim->parser_term) {
1401       (*uim->parser_term->destroy)(uim->parser_term);
1402     }
1403 
1404     if (uim->conv) {
1405       (*uim->conv->destroy)(uim->conv);
1406     }
1407 
1408     free(uim);
1409   }
1410 
1411   return NULL;
1412 }
1413 
1414 /* --- API for external tools --- */
1415 
im_uim_get_info(char * locale,char * encoding)1416 im_info_t *im_uim_get_info(char *locale, char *encoding) {
1417   im_info_t *result = NULL;
1418   uim_context u;
1419   int i;
1420   int uim_init_done;
1421 
1422   if (uim_scm_is_initialized()) {
1423     uim_init_done = 0;
1424   } else if (uim_init() == -1) {
1425 #ifdef DEBUG
1426     bl_warn_printf(BL_DEBUG_TAG " failed to initialize uim.");
1427 #endif
1428 
1429     return NULL;
1430   } else {
1431     uim_init_done = 1;
1432   }
1433 
1434   if (!(u = uim_create_context(NULL, "UTF-8", NULL, NULL, NULL, NULL))) {
1435     goto error;
1436   }
1437 
1438   if (!(result = malloc(sizeof(im_info_t)))) {
1439     goto error;
1440   }
1441 
1442   result->num_args = uim_get_nr_im(u) + 1;
1443   result->args = calloc(result->num_args, sizeof(char *));
1444   result->readable_args = calloc(result->num_args, sizeof(char *));
1445   if (result->args == NULL || result->readable_args == NULL) {
1446     goto error;
1447   }
1448 
1449   result->args[0] = strdup("");
1450   result->readable_args[0] = strdup(uim_get_default_im_name(locale));
1451 
1452   for (i = 1; i < result->num_args; i++) {
1453     const char *im_name;
1454     const char *lang_id;
1455     size_t len;
1456 
1457     im_name = uim_get_im_name(u, i - 1);
1458     lang_id = uim_get_im_language(u, i - 1);
1459 
1460     result->args[i] = strdup(im_name);
1461 
1462     len = strlen(im_name) + strlen(lang_id) + 4;
1463 
1464     if ((result->readable_args[i] = malloc(len))) {
1465       bl_snprintf(result->readable_args[i], len, "%s (%s)", im_name, lang_id);
1466     } else {
1467       result->readable_args[i] = strdup("error");
1468     }
1469   }
1470 
1471   uim_release_context(u);
1472 
1473   if (uim_init_done) {
1474     uim_quit();
1475   }
1476 
1477   result->id = strdup("uim");
1478   result->name = strdup("uim");
1479 
1480   return result;
1481 
1482 error:
1483 
1484   if (u) {
1485     uim_release_context(u);
1486   }
1487 
1488   if (uim_init_done) {
1489     uim_quit();
1490   }
1491 
1492   if (result) {
1493     if (result->args) {
1494       free(result->args);
1495     }
1496 
1497     if (result->readable_args) {
1498       free(result->readable_args);
1499     }
1500 
1501     free(result);
1502   }
1503 
1504   return NULL;
1505 }
1506