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 
34 #include <config.h>
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <m17n.h>
41 #include "gettext.h"
42 #include "uim.h"
43 #include "uim-scm.h"
44 #include "uim-scm-abbrev.h"
45 #include "uim-util.h"
46 #include "dynlib.h"
47 
48 static int m17nlib_ok;
49 static MConverter *converter;
50 static char buffer_for_converter[4096]; /* Currently, if preedit strings or
51 					   candidate strings over this buffer
52 					   size, they will simply ignore. */
53 
54 static int nr_input_methods;
55 static struct im_ {
56   char *lang;
57   char *name;
58   MInputMethod *im;
59 } *im_array;
60 
61 static int nr_input_contexts;
62 static struct ic_ {
63   MInputContext *mic;
64   char **old_candidates; /* FIXME: ugly hack for perfomance... */
65   char **new_candidates; /* FIXME: ugly hack for perfomance... */
66   int  nr_candidates;
67 } *ic_array;
68 
69 static MInputMethod *im_instance(int nth);
70 
71 /* Utility function */
72 static char *
m17nlib_utf8_find_next_char(const char * p)73 m17nlib_utf8_find_next_char(const char *p)
74 {
75   if (*p) {
76     for (++p; (*p & 0xc0) == 0x80; ++p)
77       ;
78   }
79   return (char *)p;
80 }
81 
82 static int
unused_ic_id(void)83 unused_ic_id(void)
84 {
85   int i;
86 
87   for (i = 0; i < nr_input_contexts; i++) {
88     if (!ic_array[i].mic)
89       return i;
90   }
91 
92   ic_array = uim_realloc(ic_array,
93 			 sizeof(struct ic_) * (nr_input_contexts + 1));
94   ic_array[nr_input_contexts].mic = NULL;
95   nr_input_contexts++;
96 
97   return nr_input_contexts - 1;
98 }
99 
100 static void
pushback_input_method(MInputMethod * im,char * lang,char * name)101 pushback_input_method(MInputMethod *im, char *lang, char *name)
102 {
103   im_array = uim_realloc(im_array,
104 			 sizeof(struct im_) * (nr_input_methods + 1));
105   im_array[nr_input_methods].im = im;
106   im_array[nr_input_methods].name = uim_strdup(name);
107   im_array[nr_input_methods].lang = uim_strdup(lang);
108 
109   nr_input_methods++;
110 }
111 
112 #if 0
113 static void
114 preedit_start_cb(MInputContext *ic, MSymbol command)
115 {
116   fprintf(stderr,"preedit start\n");
117 }
118 
119 static void
120 preedit_draw_cb(MInputContext *ic, MSymbol command)
121 {
122 
123 }
124 
125 static void
126 preedit_done_cb(MInputContext *ic, MSymbol command)
127 {
128   /*  fprintf(stderr,"preedit done\n");*/
129 }
130 
131 static void
132 status_start_cb(MInputContext *ic, MSymbol command)
133 {
134   /*  fprintf(stderr,"status start\n");*/
135 }
136 
137 static void
138 status_draw_cb(MInputContext *ic, MSymbol command)
139 {
140   /*  fprintf(stderr,"status draw\n"); */
141 }
142 
143 static void
144 status_done_cb(MInputContext *ic, MSymbol command)
145 {
146   /*  fprintf(stderr,"status done\n");*/
147 }
148 
149 static void
150 candidates_start_cb(MInputContext *ic, MSymbol command)
151 {
152   /*  fprintf(stderr,"candidates_start\n");*/
153 }
154 
155 static void
156 candidates_draw_cb(MInputContext *ic, MSymbol command)
157 {
158   /*  fprintf(stderr,"candidate draw\n"); */
159 }
160 
161 static void
162 candidates_done_cb(MInputContext *ic, MSymbol command)
163 {
164   /*  fprintf(stderr,"candidate done\n"); */
165 }
166 
167 static void
168 register_callbacks(void)
169 {
170   /*
171   mplist_add(minput_default_driver.callback_list, Minput_preedit_start, (void *)preedit_start_cb);
172   mplist_add(minput_default_driver.callback_list, Minput_preedit_draw,  (void *)preedit_draw_cb);
173   mplist_add(minput_default_driver.callback_list, Minput_preedit_done,  (void *)preedit_done_cb);
174     mplist_add(minput_default_driver.callback_list, Minput_status_start,  (void *)status_start_cb);
175   mplist_add(minput_default_driver.callback_list, Minput_status_draw,   (void *)status_draw_cb);
176   mplist_add(minput_default_driver.callback_list, Minput_status_done,   (void *)status_done_cb);
177   mplist_add(minput_default_driver.callback_list, Minput_candidates_start, (void *)candidates_start_cb);
178   mplist_add(minput_default_driver.callback_list, Minput_candidates_draw,  (void *)candidates_draw_cb);
179   mplist_add(minput_default_driver.callback_list, Minput_candidates_done,  (void *)candidates_done_cb);*/
180 }
181 #endif
182 
183 static uim_lisp
init_m17nlib()184 init_m17nlib()
185 {
186   MPlist *imlist, *elm;
187 
188   M17N_INIT();
189   nr_input_methods = 0;
190   nr_input_contexts = 0;
191   im_array = NULL;
192   ic_array = NULL;
193 
194   imlist = mdatabase_list(msymbol("input-method"), Mnil, Mnil, Mnil);
195 
196   if (!imlist) {
197     /* maybe user forgot to install m17n-db */
198     return uim_scm_f();
199   }
200 
201   for (elm = imlist; mplist_key(elm) != Mnil; elm = mplist_next(elm)) {
202     MDatabase *mdb;
203     MSymbol *tag, lang, imname;
204     uim_bool is_complete_im;
205 
206     mdb = mplist_value(elm);
207     tag = mdatabase_tag(mdb);
208     lang = tag[1];
209     imname = tag[2];
210     is_complete_im = (lang != Mnil && imname != Mnil);  /* [uim-ja 30] */
211 
212     if (is_complete_im) {
213       /* pass NULL as IM to enable lazy instantiation */
214       pushback_input_method(NULL, msymbol_name(lang), msymbol_name(imname));
215     }
216   }
217 #if 0
218   register_callbacks();
219 #endif
220   m17n_object_unref(imlist);
221   converter = mconv_buffer_converter(msymbol("utf8"), NULL, 0);
222 
223   if (!converter)
224     return uim_scm_f();
225 
226   m17nlib_ok = 1;
227 
228   return uim_scm_t();
229 }
230 
231 static char *
convert_mtext2str(MText * mtext)232 convert_mtext2str(MText *mtext)
233 {
234   mconv_rebind_buffer(converter, (unsigned char *)buffer_for_converter,
235 		      sizeof(buffer_for_converter)-1);
236   mconv_encode(converter, mtext);
237   buffer_for_converter[converter->nbytes] = 0;
238 
239   return uim_strdup(buffer_for_converter);
240 }
241 
242 static uim_lisp
compose_modep(uim_lisp id_)243 compose_modep(uim_lisp id_)
244 {
245   int id;
246   MInputContext *ic;
247 
248   id = C_INT(id_);
249   ic = ic_array[id].mic;
250 
251   if (!ic)
252     return uim_scm_f();
253 
254   if (ic->candidate_from == ic->candidate_to
255       || ic->candidate_from > ic->candidate_to)
256     return uim_scm_f();
257   else
258     return uim_scm_t();
259 }
260 
261 static uim_lisp
preedit_changedp(uim_lisp id_)262 preedit_changedp(uim_lisp id_)
263 {
264   int id;
265   MInputContext *ic;
266 
267   id = C_INT(id_);
268   ic = ic_array[id].mic;
269 
270   if (!ic)
271     return uim_scm_f();
272 
273   if (ic->preedit_changed == 1)
274     return uim_scm_t();
275   else
276     return uim_scm_f();
277 }
278 
279 static uim_lisp
get_left_of_cursor(uim_lisp id_)280 get_left_of_cursor(uim_lisp id_)
281 {
282   int id, i;
283   uim_lisp buf_;
284   char *buf, *p;
285   MInputContext *ic;
286 
287   id = C_INT(id_);
288   ic = ic_array[id].mic;
289 
290   if (!ic)
291     return MAKE_STR("");
292 
293   if (ic->cursor_pos == 0)
294     return MAKE_STR("");
295 
296   buf = convert_mtext2str(ic->preedit);
297   p = buf;
298 
299   for (i = 0; i < ic->cursor_pos ;i++)
300     p = m17nlib_utf8_find_next_char(p);
301   *p = '\0';
302 
303   buf_ = MAKE_STR_DIRECTLY(buf);
304 
305   return buf_;
306 }
307 
308 static uim_lisp
get_right_of_cursor(uim_lisp id_)309 get_right_of_cursor(uim_lisp id_)
310 {
311   int id, i;
312   uim_lisp buf_;
313   char *buf, *p;
314   MInputContext *ic;
315 
316   id = C_INT(id_);
317   ic = ic_array[id].mic;
318 
319   if (!ic)
320     return MAKE_STR("");
321 
322   buf = convert_mtext2str(ic->preedit);
323   p = buf;
324 
325   for (i = 0; i < ic->cursor_pos ;i++)
326     p = m17nlib_utf8_find_next_char(p);
327 
328   buf_ = MAKE_STR(p);
329   free(buf);
330 
331   return buf_;
332 }
333 
334 static uim_lisp
get_left_of_candidate(uim_lisp id_)335 get_left_of_candidate(uim_lisp id_)
336 {
337   int id, i;
338   uim_lisp buf_;
339   char *buf, *p;
340   MInputContext *ic;
341 
342   id = C_INT(id_);
343   ic = ic_array[id].mic;
344 
345   if (!ic)
346     return MAKE_STR("");
347 
348   if (ic->candidate_from == 0)
349     return MAKE_STR("");
350 
351   buf = convert_mtext2str(ic->preedit);
352   p = buf;
353 
354   for (i = 0; i < ic->candidate_from ;i++)
355     p = m17nlib_utf8_find_next_char(p);
356   *p = '\0';
357 
358   buf_ = MAKE_STR_DIRECTLY(buf);
359 
360   return buf_;
361 }
362 
363 static uim_lisp
get_selected_candidate(uim_lisp id_)364 get_selected_candidate(uim_lisp id_)
365 {
366   int id, i;
367   uim_lisp buf_;
368   char *buf, *p, *start;
369   MInputContext *ic;
370 
371   id = C_INT(id_);
372   ic = ic_array[id].mic;
373 
374   if (!ic)
375     return MAKE_STR("");
376 
377   buf = convert_mtext2str(ic->preedit);
378   p = buf;
379 
380   if (!p)
381     return MAKE_STR("");
382 
383   for (i = 0; i < ic->candidate_from ;i++)
384     p = m17nlib_utf8_find_next_char(p);
385   start = p;
386 
387   for (i = 0; i < ic->candidate_to - ic->candidate_from ;i++)
388     p = m17nlib_utf8_find_next_char(p);
389   *p = '\0';
390 
391   buf_ = MAKE_STR(start);
392   free(buf);
393 
394   return buf_;
395 }
396 
397 static uim_lisp
get_right_of_candidate(uim_lisp id_)398 get_right_of_candidate(uim_lisp id_)
399 {
400   int id, i;
401   uim_lisp buf_;
402   char *buf, *p;
403   MInputContext *ic;
404 
405   id = C_INT(id_);
406   ic = ic_array[id].mic;
407 
408   if (!ic)
409     return MAKE_STR("");
410 
411   buf = convert_mtext2str(ic->preedit);
412   p = buf;
413 
414   for (i = 0; i < ic->candidate_to ;i++)
415     p = m17nlib_utf8_find_next_char(p);
416 
417   buf_ = MAKE_STR(p);
418   free(buf);
419 
420   return buf_;
421 }
422 
423 static uim_lisp
get_nr_input_methods()424 get_nr_input_methods()
425 {
426   return MAKE_INT(nr_input_methods);
427 }
428 
429 static uim_lisp
get_input_method_name(uim_lisp nth_)430 get_input_method_name(uim_lisp nth_)
431 {
432   int nth;
433   char name[BUFSIZ];
434 
435   nth = C_INT(nth_);
436 
437   if (nth < nr_input_methods) {
438     if (!strcmp(im_array[nth].lang, "t"))
439       snprintf(name, sizeof(name), "m17n-%s", im_array[nth].name);
440     else
441       snprintf(name, sizeof(name), "m17n-%s-%s", im_array[nth].lang, im_array[nth].name);
442 
443     return MAKE_STR(name);
444   }
445 
446   return uim_scm_f();
447 }
448 
449 static uim_lisp
get_input_method_lang(uim_lisp nth_)450 get_input_method_lang(uim_lisp nth_)
451 {
452   int nth;
453   const char *lang;
454 
455   nth = C_INT(nth_);
456 
457   if (nth < nr_input_methods) {
458     lang = im_array[nth].lang;
459     /* "*" is wildcard language. See langgroup-covers? and
460      * find-im-for-locale. */
461     return MAKE_STR((strcmp(lang, "t") == 0) ? "*" : lang);
462   }
463 
464   return uim_scm_f();
465 }
466 
467 static uim_lisp
get_input_method_short_desc(uim_lisp nth_)468 get_input_method_short_desc(uim_lisp nth_)
469 {
470   int nth;
471   char *str = NULL, *p;
472   uim_lisp ret;
473 
474   nth = C_INT(nth_);
475 
476   if (nth < nr_input_methods) {
477     MInputMethod *im;
478     MText *desc;
479 
480     im = im_instance(nth);
481     if (!im)
482       return MAKE_STR(N_("m17n library IM open error"));
483 
484     desc = minput_get_description(im->language, im->name);
485     if (desc) {
486       int i, len;
487 
488       str = convert_mtext2str(desc);
489       p = strchr(str, '.');
490       if (p)
491 	*p = '\0';
492       len = strlen(str);
493 
494       /*
495        * Workaround for the descriptions which lack period.
496        * Also we avoid the description with non English words.
497        * See https://bugs.freedesktop.org/show_bug.cgi?id=6972
498        */
499       for (i = 0; i < len; i++) {
500 	if (str[i] == '\n') {
501 	  str[i] = '\0';
502 	  break;
503 	}
504 #ifdef HAVE_ISASCII
505 	else if (!isascii((int)str[i])) {
506 #else
507 	else if ((int)str[i] & ~0x7f) {
508 #endif
509 	  free(str);
510 	  str = NULL;
511 	  break;
512 	}
513       }
514       m17n_object_unref(desc);
515     }
516 
517     if (str) {
518       ret = MAKE_STR(str);
519       free(str);
520     } else {
521       ret = MAKE_STR(N_("An input method provided by the m17n library"));
522     }
523   } else
524     ret = uim_scm_f();
525 
526   return ret;
527 }
528 
529 static MInputMethod *
530 im_instance(int nth)
531 {
532   struct im_ *ent;
533 
534   if (!(0 <= nth && nth < nr_input_methods))
535     return NULL;
536 
537   ent = &im_array[nth];
538   if (!ent->im)
539     ent->im = minput_open_im(msymbol(ent->lang), msymbol(ent->name), NULL);
540 
541   return ent->im;
542 }
543 
544 static MInputMethod *
545 find_im_by_name(const char *name)
546 {
547   int i;
548   const char *im_name;
549 
550   if (strncmp(name, "m17n-", 5) != 0)
551     return NULL;
552 
553   im_name = &name[5];
554 
555   for (i = 0; i < nr_input_methods; i++) {
556     char buf[100];
557 
558     if (!strcmp(im_array[i].lang, "t"))
559       strlcpy(buf, im_array[i].name, sizeof(buf));
560     else
561       snprintf(buf, sizeof(buf), "%s-%s", im_array[i].lang, im_array[i].name);
562 
563     if (!strcmp(im_name, buf))
564       return im_instance(i);
565   }
566 
567   return NULL;
568 }
569 
570 static uim_lisp
571 alloc_id(uim_lisp name_)
572 {
573   int id;
574   const char *name;
575   MInputMethod *im;
576 
577   id = unused_ic_id();
578   name = REFER_C_STR(name_);
579 
580   im = find_im_by_name(name);
581 
582   if (im)
583     ic_array[id].mic = minput_create_ic(im, NULL);
584 
585   ic_array[id].old_candidates = NULL;
586   ic_array[id].new_candidates = NULL;
587 
588   return MAKE_INT(id);
589 }
590 
591 static uim_lisp
592 free_id(uim_lisp id_)
593 {
594   int id = C_INT(id_);
595 
596   if (id < nr_input_contexts) {
597     struct ic_ *ic = &ic_array[id];
598 
599     if (ic->mic) {
600       minput_destroy_ic(ic->mic);
601       ic->mic = NULL;
602     }
603   }
604 
605   return uim_scm_f();
606 }
607 
608 static uim_lisp
609 push_symbol_key(uim_lisp id_, uim_lisp key_)
610 {
611   int id;
612   MSymbol key;
613   MInputContext *ic;
614 
615   id = C_INT(id_);
616   ic = ic_array[id].mic;
617   key = msymbol(C_STR(key_));
618 
619   if (key == Mnil)
620     return uim_scm_f();
621 
622   if (minput_filter(ic, key, NULL) == 1)
623     return uim_scm_t();
624   else
625     return uim_scm_f();
626 }
627 
628 static uim_lisp
629 get_result(uim_lisp id_)
630 {
631   MText *produced;
632   char *commit_string;
633   int consumed, id;
634   MInputContext *ic;
635   uim_lisp  consumed_, commit_string_;
636 
637   id = C_INT(id_);
638   ic = ic_array[id].mic;
639 
640   produced  = mtext();
641   consumed  = minput_lookup(ic, NULL, NULL, produced);
642 
643   if (consumed == -1)
644     consumed_ = uim_scm_f();
645   else
646     consumed_ = uim_scm_t();
647 
648   commit_string = convert_mtext2str(produced);
649   m17n_object_unref(produced);
650   commit_string_ = MAKE_STR(commit_string);
651   free(commit_string);
652 
653   return CONS(consumed_, commit_string_);
654 }
655 
656 static uim_lisp
657 commit(uim_lisp id_)
658 {
659   int id;
660   MInputContext *ic;
661 
662   id = C_INT(id_);
663   ic = ic_array[id].mic;
664 
665   /* To avoid a bug of m17n-lib */
666   ic->candidate_to = 0;
667 
668   return uim_scm_f();
669 }
670 
671 static uim_lisp
672 candidate_showp(uim_lisp id_)
673 {
674   int id;
675   MInputContext *ic;
676 
677   id = C_INT(id_);
678   ic = ic_array[id].mic;
679 
680   if (ic->candidate_show == 1)
681     return uim_scm_t();
682 
683   return uim_scm_f();
684 }
685 
686 static int
687 calc_cands_num(int id)
688 {
689   int result = 0;
690   MPlist *group;
691   MInputContext *ic;
692 
693   ic = ic_array[id].mic;
694 
695   if (!ic || !ic->candidate_list)
696     return 0;
697 
698   group = ic->candidate_list;
699 
700   while (mplist_value(group) != Mnil) {
701     if (mplist_key(group) == Mtext) {
702       for (; mplist_key(group) != Mnil; group = mplist_next(group))
703 	result += mtext_len(mplist_value(group));
704     } else {
705       for (; mplist_key(group) != Mnil; group = mplist_next(group))
706 	result += mplist_length(mplist_value(group));
707     }
708   }
709 
710   return result;
711 }
712 
713 static void
714 old_cands_free(char **old_cands)
715 {
716   int i = 0;
717 
718   if (old_cands) {
719     for (i = 0; old_cands[i]; i++)
720       free(old_cands[i]);
721     free(old_cands);
722   }
723 }
724 
725 static uim_lisp
726 fill_new_candidates(uim_lisp id_)
727 {
728   MText *produced;
729   MPlist *group, *elm;
730   int i, id, cands_num;
731   char **new_cands;
732   MInputContext *ic;
733 
734   id = C_INT(id_);
735   ic = ic_array[id].mic;
736   cands_num = calc_cands_num(id);
737 
738   if (!ic || !ic->candidate_list)
739     return uim_scm_f();
740 
741   group = ic->candidate_list;
742 
743   old_cands_free(ic_array[id].old_candidates);
744   ic_array[id].old_candidates = ic_array[id].new_candidates;
745 
746   new_cands = uim_malloc(cands_num * sizeof(char *) + 2);
747 
748   if (mplist_key(group) == Mtext) {
749     for (i = 0; mplist_key(group) != Mnil; group = mplist_next(group)) {
750       int j;
751       for (j = 0; j < mtext_len(mplist_value(group)); j++, i++) {
752 	  produced = mtext();
753 	  mtext_cat_char(produced, mtext_ref_char(mplist_value(group), j));
754 	  new_cands[i] = convert_mtext2str(produced);
755 	  m17n_object_unref(produced);
756       }
757     }
758   } else {
759     for (i = 0; mplist_key(group) != Mnil; group = mplist_next(group)) {
760 
761       for (elm = mplist_value(group); mplist_key(elm) != Mnil;
762 	   elm = mplist_next(elm),i++) {
763 	produced = mplist_value(elm);
764 	new_cands[i] = convert_mtext2str(produced);
765       }
766     }
767   }
768   new_cands[i] = NULL;
769 
770   ic_array[id].new_candidates = new_cands;
771   ic_array[id].nr_candidates = i;
772 
773   return uim_scm_t();
774 }
775 
776 static uim_bool
777 same_candidatesp(char **old, char **new)
778 {
779   int i;
780 
781   if (!old)
782     return UIM_FALSE;
783 
784   for (i = 0; old[i] && new[i]; i++) {
785     if (strcmp(old[i], new[i]) != 0)
786       return UIM_FALSE;
787   }
788 
789   return UIM_TRUE;
790 }
791 
792 static uim_lisp
793 candidates_changedp(uim_lisp id_)
794 {
795   int id = C_INT(id_);
796 
797   if (!same_candidatesp(ic_array[id].old_candidates,
798 			ic_array[id].new_candidates))
799     return uim_scm_t();
800 
801   return uim_scm_f();
802 }
803 
804 static uim_lisp
805 get_nr_candidates(uim_lisp id_)
806 {
807   int id = C_INT(id_);
808 
809   return MAKE_INT(calc_cands_num(id));
810 }
811 
812 static uim_lisp
813 get_nth_candidate(uim_lisp id_, uim_lisp nth_)
814 {
815   int id, nth, nr;
816 
817   id = C_INT(id_);
818   nth = C_INT(nth_);
819   nr = ic_array[id].nr_candidates;
820 
821   if (nr >= nth)
822     return MAKE_STR(ic_array[id].new_candidates[nth]);
823   else
824     return MAKE_STR("");
825 }
826 
827 static uim_lisp
828 get_candidate_index(uim_lisp id_)
829 {
830   int id;
831   MInputContext *ic;
832 
833   id = C_INT(id_);
834   ic = ic_array[id].mic;
835 
836   return MAKE_INT(ic->candidate_index);
837 }
838 
839 void
840 uim_plugin_instance_init(void)
841 {
842   uim_scm_init_proc0("m17nlib-lib-init", init_m17nlib);
843   uim_scm_init_proc0("m17nlib-lib-nr-input-methods",
844 		     get_nr_input_methods);
845   uim_scm_init_proc1("m17nlib-lib-nth-input-method-lang",
846 		     get_input_method_lang);
847   uim_scm_init_proc1("m17nlib-lib-nth-input-method-name",
848 		     get_input_method_name);
849   uim_scm_init_proc1("m17nlib-lib-nth-input-method-short-desc",
850 		     get_input_method_short_desc);
851   uim_scm_init_proc1("m17nlib-lib-alloc-context", alloc_id);
852   uim_scm_init_proc1("m17nlib-lib-free-context", free_id);
853   uim_scm_init_proc2("m17nlib-lib-push-symbol-key", push_symbol_key);
854   uim_scm_init_proc1("m17nlib-lib-compose-mode?", compose_modep);
855   uim_scm_init_proc1("m17nlib-lib-preedit-changed?", preedit_changedp);
856   uim_scm_init_proc1("m17nlib-lib-get-left-of-cursor", get_left_of_cursor);
857   uim_scm_init_proc1("m17nlib-lib-get-right-of-cursor", get_right_of_cursor);
858   uim_scm_init_proc1("m17nlib-lib-get-left-of-candidate",
859 		     get_left_of_candidate);
860   uim_scm_init_proc1("m17nlib-lib-get-selected-candidate",
861 		     get_selected_candidate);
862   uim_scm_init_proc1("m17nlib-lib-get-right-of-candidate",
863 		     get_right_of_candidate);
864   uim_scm_init_proc1("m17nlib-lib-get-result", get_result);
865   uim_scm_init_proc1("m17nlib-lib-commit", commit);
866   uim_scm_init_proc1("m17nlib-lib-candidate-show?", candidate_showp);
867   uim_scm_init_proc1("m17nlib-lib-fill-new-candidates!", fill_new_candidates);
868   uim_scm_init_proc1("m17nlib-lib-candidates-changed?", candidates_changedp);
869   uim_scm_init_proc1("m17nlib-lib-get-nr-candidates", get_nr_candidates);
870   uim_scm_init_proc2("m17nlib-lib-get-nth-candidate", get_nth_candidate);
871   uim_scm_init_proc1("m17nlib-lib-get-candidate-index", get_candidate_index);
872 }
873 
874 void
875 uim_plugin_instance_quit(void)
876 {
877   if (converter) {
878     mconv_free_converter(converter);
879     converter = NULL;
880   }
881   if (m17nlib_ok) {
882     M17N_FINI();
883     m17nlib_ok = 0;
884     free(im_array);
885     free(ic_array);
886   }
887 }
888