Modified Files:
[elisp/tamago.git] / egg / canna.el
1 ;;; egg/canna.el --- Canna Support (high level interface) in
2 ;;;                  Egg Input Method Architecture
3
4 ;; Copyright (C) 1999, 2000 Free Software Foundation, Inc
5
6 ;; Author: NIIBE Yutaka <gniibe@chroot.org>
7
8 ;; Maintainer: TOMURA Satoru <tomura@etl.go.jp>
9
10 ;; Keywords: mule, multilingual, input method
11
12 ;; This file is part of EGG.
13
14 ;; EGG is free software; you can redistribute it and/or modify
15 ;; it under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation; either version 2, or (at your option)
17 ;; any later version.
18
19 ;; EGG is distributed in the hope that it will be useful,
20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 ;; GNU General Public License for more details.
23
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
26 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
27 ;; Boston, MA 02111-1307, USA.
28
29 ;;; Commentary:
30
31 ;;; Code:
32
33 (require 'egg)
34 (require 'egg-edep)
35
36 (defgroup canna nil
37   "CANNA interface for Tamago 4."
38   :group 'egg)
39
40 (defcustom canna-hostname "localhost"
41   "Hostname of CANNA server"
42   :group 'canna :type 'string)
43
44 (defcustom canna-server-port "canna"
45   "A service name or a port number (should be a string) of CANNA server"
46   :group 'canna :type 'string)
47
48 (defcustom canna-user-name nil
49   "User Name on CANNA server"
50   :group 'canna :type 'string)
51
52 (defcustom canna-group-name nil
53   "Group Name on CANNA server"
54   :group 'canna :type 'string)
55
56 ; (eval-when-compile
57 ;   (defmacro CANNA-const (c)
58 ;     (cond ((eq c 'FileNotExist) xxxxxxxxxxxxxx)
59 ;         )))
60
61 (egg-add-message
62  '((Japanese
63     (canna-connect-error  "\e$B%5!<%P$H@\B3$G$-$^$;$s$G$7$?\e(B")
64     (canna-fail-make-env  "\e$B4D6-$r:n$k$3$H$O$G$-$^$;$s$G$7$?\e(B")
65     (canna-dict-missing-1 "\e$B<-=q%U%!%$%k\e(B %s \e$B$,$"$j$^$;$s!#\e(B")
66     (canna-dict-missing-2 "\e$B<-=q%U%!%$%k\e(B %s \e$B$,$"$j$^$;$s!#:n$j$^$9$+\e(B? ")
67     (canna-dict-created   "\e$B<-=q%U%!%$%k\e(B %s \e$B$r:n$j$^$7$?\e(B")
68     (canna-dict-saving    "%s \e$B$NIQEY>pJs!&<-=q>pJs$rB`Hr$7$F$$$^$9\e(B")
69     (canna-dict-saved     "%s \e$B$NIQEY>pJs!&<-=q>pJs$rB`Hr$7$^$7$?\e(B")
70     (canna-register-1     "\e$BEPO?<-=qL>\e(B:")
71     (canna-register-2     "\e$BIJ;lL>\e(B"))))
72
73 (defvar canna-hinshi-alist
74   '(("\e$B?ML>\e(B" . "#JN") ("\e$BCOL>\e(B" . "#CN") ("\e$B8GM-L>;l\e(B" . "#KK")
75     ("\e$B0lHLL>;l\e(B" . "#T35") ("\e$BL>;l\e(B(\e$BNc\e(B)\e$B6/NO$J\e(B" . "#T15")
76     ("\e$B%5JQL>;l\e(B" . "#T30") ("\e$B%5JQL>;l\e(B(\e$BNc\e(B)\e$B0B?4$J\e(B" . "#T10") ("\e$BC14A;z\e(B" . "#KJ")
77     ("\e$BF0;l%+9TJQ3J3hMQ\e(B" . "#KX") ("\e$BF0;l%s%69TJQ3J3hMQ\e(B" . "#NZX")
78     ("\e$BF0;l%69TJQ3J3hMQ\e(B" . "#ZX") ("\e$BF0;l%59TJQ3J3hMQ\e(B" . "#SX")
79     ("\e$BF0;l%+9T8^CJ3hMQ\e(B" . "#K5") ("\e$BF0;l%,9T8^CJ3hMQ\e(B" . "#G5")
80     ("\e$BF0;l%59T8^CJ3hMQ\e(B" . "#S5") ("\e$BF0;l%?9T8^CJ3hMQ\e(B" . "#T5")
81     ("\e$BF0;l%J9T8^CJ3hMQ\e(B" . "#N5") ("\e$BF0;l%P9T8^CJ3hMQ\e(B" . "#B5")
82     ("\e$BF0;l%^9T8^CJ3hMQ\e(B" . "#M5") ("\e$BF0;l%i9T8^CJ3hMQ\e(B" . "#R5")
83     ("\e$BF0;l%o9T8^CJ3hMQ\e(B" . "#W5") ("\e$BF0;l>e2<0lCJ3hMQ\e(B" . "#KS")
84     ("\e$BF0;l%+9T8^CJO"MQL>;l\e(B" . "#K5r") ("\e$BF0;l%,9T8^CJO"MQL>;l\e(B" . "#G5r")
85     ("\e$BF0;l%59T8^CJO"MQL>;l\e(B" . "#S5r") ("\e$BF0;l%?9T8^CJO"MQL>;l\e(B" . "#T5r")
86     ("\e$BF0;l%J9T8^CJO"MQL>;l\e(B" . "#N5r") ("\e$BF0;l%P9T8^CJO"MQL>;l\e(B" . "#B5r")
87     ("\e$BF0;l%^9T8^CJO"MQL>;l\e(B" . "#M5r") ("\e$BF0;l%i9T8^CJO"MQL>;l\e(B" . "#R5r")
88     ("\e$BF0;l%o9T8^CJO"MQL>;l\e(B" . "#W5r") ("\e$BF0;l>e2<0lCJ8l44L>;l\e(B" . "#KSr")
89     ("\e$B7AMF;l\e(B" . "#KY") ("\e$B7AMF;l\e(B(\e$BNc\e(B)\e$B$-$$$m$$\e(B" . "#KYT")
90     ("\e$B7AMFF0;l\e(B" . "#T05")
91     ("\e$B7AMFF0;l\e(B(\e$BNc\e(B)\e$B4X?4$@\e(B" . "#T10") ("\e$B7AMFF0;l\e(B(\e$BNc\e(B)\e$BB?92$F$@\e(B" . "#T13")
92     ("\e$B7AMFF0;l\e(B(\e$BNc\e(B)\e$B0U30$@\e(B" . "#T15") ("\e$B7AMFF0;l\e(B(\e$BNc\e(B)\e$BJXMx$@\e(B" . "#T18")
93     ("\e$BI{;l\e(B" . "#F14") ("\e$BI{;l\e(B(\e$BNc\e(B)\e$B$U$C$/$i\e(B" . "#F04")
94     ("\e$BI{;l\e(B(\e$BNc\e(B)\e$B$=$C$H\e(B" . "#F12") ("\e$BI{;l\e(B(\e$BNc\e(B)\e$BFMA3\e(B" . "#F06")
95     ("\e$B?t;l\e(B" . "#NN") ("\e$B@\B3;l!&46F0;l\e(B" . "#CJ") ("\e$BO"BN;l\e(B" . "#RT")))
96
97 (defvar canna-hinshi-menu
98   '("\e$B?ML>\e(B" "\e$BCOL>\e(B" ("\e$BCDBN!&2q<RL>\e(B" . "\e$B8GM-L>;l\e(B") ("\e$BL>;l\e(B" . MEISHI)
99     ("\e$B%5JQL>;l\e(B" . SAHEN-MEISHI) "\e$BC14A;z\e(B" ("\e$BF0;l\e(B" . DOUSHI)
100     ("\e$B7AMF;l\e(B" . KEIYOUSHI) ("\e$B7AMFF0;l\e(B" . KEIYOUDOUSHI) ("\e$BI{;l\e(B" . FUKUSHI)
101     "\e$B?t;l\e(B" "\e$B@\B3;l!&46F0;l\e(B" "\e$BO"BN;l\e(B" ("\e$B$=$NB>$N8GM-L>;l\e(B" . "\e$B8GM-L>;l\e(B"))
102   "Menu data for a hinshi (a part of speech) selection.")
103
104 (defun canna-hinshi-name (id &optional reverse)
105   (if reverse
106       (cdr (assoc id canna-hinshi-alist))
107     (car (rassoc id canna-hinshi-alist))))
108
109 (defmacro canna-backend-plist ()
110   ''(egg-start-conversion          canna-start-conversion
111      egg-get-bunsetsu-source       canna-get-bunsetsu-source
112      egg-get-bunsetsu-converted    canna-get-bunsetsu-converted
113      egg-get-source-language       canna-get-source-language
114      egg-get-converted-language    canna-get-converted-language
115      egg-list-candidates           canna-list-candidates
116      egg-decide-candidate          canna-decide-candidate
117      egg-special-candidate         canna-special-candidate
118      egg-change-bunsetsu-length    canna-change-bunsetsu-length
119      egg-end-conversion            canna-end-conversion
120      egg-word-registration         canna-word-registration))
121
122 (defconst canna-backend-language-alist nil)
123
124 (defvar canna-backend-alist nil)
125
126 (defun canna-backend-func-name (name lang &optional env)
127   (intern (concat name "-" (symbol-name lang)
128                   (and env "-") (and env (symbol-name env)))))
129
130 (defun canna-make-backend (lang env &optional source-lang converted-lang)
131   (let ((finalize (canna-backend-func-name "canna-finalize-backend" lang))
132         (backend (canna-backend-func-name "canna-backend" lang env)))
133     (if (null (fboundp finalize))
134         (progn
135           (fset finalize (function (lambda () (canna-finalize-backend))))
136           (egg-set-finalize-backend (list finalize))))
137     (if (null (get backend 'egg-start-conversion))
138         (setplist backend (apply 'list
139                                  'language lang
140                                  'source-language (or source-lang lang)
141                                  'converted-language (or converted-lang lang)
142                                  (canna-backend-plist))))
143     backend))
144
145 (defun canna-define-backend (lang env-name-list)
146   (mapcar (lambda (env)
147             (if (consp env)
148                 (canna-define-backend lang env)
149               (canna-make-backend lang env)))
150           env-name-list))
151
152 (defun canna-define-backend-alist (deflist)
153   (setq canna-backend-alist
154         (mapcar (lambda (slot)
155                   (let* ((lang (car slot))
156                          (alt (cdr (assq lang canna-backend-language-alist))))
157                     (cons lang (canna-define-backend (or alt lang) (cdr slot)))))
158                 deflist)))
159
160 (defcustom canna-backend-define-list
161   '((Japanese    ((nil nil nil))
162                  ((Bushu Bushu Bushu))))
163   "Alist of Japanese language and lists of the Canna backend suffixes."
164   :group 'canna
165   :set (lambda (sym value)
166          (set-default sym value)
167          (canna-define-backend-alist value))
168   :type '(repeat
169           (cons
170            :tag "Language - Backend"
171            (choice :tag "Language"
172                    (const Japanese)
173                    (symbol :tag "Other"))
174            (repeat
175             (cons
176              :tag "Backend Sequece"
177              (cons :tag "First Conversion Stage"
178                    (symbol :tag "Backend for Start Conversion")
179                    (repeat :tag "Backends for Reconvert"
180                            (symbol :tag "Backend")))
181              (repeat
182               :tag "Following Conversion Stages"
183               (cons
184                :tag "N-th Stage"
185                (symbol :tag "Backend for This Stage")
186                (repeat :tag "Backends for Reconvert"
187                        (symbol :tag "Backend")))))))))
188
189 (defsubst canna-backend-get-language (backend)
190   (get backend 'language))
191
192 (defsubst canna-backend-get-source-language (backend)
193   (get backend 'source-language))
194
195 (defsubst canna-backend-get-converted-language (backend)
196   (get backend 'converted-language))
197
198 (defvar canna-envspec-list nil)
199 (defvar canna-current-envspec nil)
200
201 ;; Should support multiple outstanding context
202 ;; <env> ::= [ <proc> <context> <backend> <convert-mode> <nostudy> <dic-list> ]
203 (defvar canna-environments nil
204   "Environment for CANNA kana-kanji conversion")
205
206 (defun cannaenv-create (proc context &optional backend mode nostudy)
207   (vector proc context backend mode nostudy (list nil)))
208
209 (defsubst cannaenv-get-proc (env)    (aref env 0))
210 (defsubst cannaenv-get-context (env) (aref env 1))
211 (defsubst cannaenv-get-backend (env) (aref env 2))
212 (defsubst cannaenv-get-mode (env)    (aref env 3))
213 (defsubst cannaenv-get-nostudy (env) (aref env 4))
214 (defsubst cannaenv-get-dic-list (env) (cdr (aref env 5)))
215
216 (defsubst cannaenv-add-dic-list (env &rest dic)
217   (nconc (aref env 5) (list (apply 'vector dic))))
218
219 ;; <canna-bunsetsu> ::=
220 ;;  [ <env> <converted> <bunsetsu-pos> <source>
221 ;;    <zenkouho-pos> <zenkouho> <zenkouho-converted> ]
222 (defsubst canna-make-bunsetsu (env converted bunsetsu-pos source)
223   (egg-bunsetsu-create
224    (cannaenv-get-backend env)
225    (vector env converted bunsetsu-pos source nil nil nil)))
226
227 (defsubst canna-bunsetsu-get-env (b)
228   (aref (egg-bunsetsu-get-info b) 0))
229 (defsubst canna-bunsetsu-get-converted (b)
230   (aref (egg-bunsetsu-get-info b) 1))
231 (defsubst canna-bunsetsu-get-bunsetsu-pos (b)
232   (aref (egg-bunsetsu-get-info b) 2))
233 (defsubst canna-bunsetsu-get-source (b)
234   (aref (egg-bunsetsu-get-info b) 3))
235 (defsubst canna-bunsetsu-set-source (b s)
236   (aset (egg-bunsetsu-get-info b) 3 s))
237 (defsubst canna-bunsetsu-get-zenkouho-pos (b)
238   (aref (egg-bunsetsu-get-info b) 4))
239 (defsubst canna-bunsetsu-set-zenkouho-pos (b p)
240   (aset (egg-bunsetsu-get-info b) 4 p))
241 (defsubst canna-bunsetsu-get-zenkouho (b)
242   (aref (egg-bunsetsu-get-info b) 5))
243 (defsubst canna-bunsetsu-set-zenkouho (b z)
244   (aset (egg-bunsetsu-get-info b) 5 z))
245 (defsubst canna-bunsetsu-get-zenkouho-converted (b)
246   (aref (egg-bunsetsu-get-info b) 6))
247 (defsubst canna-bunsetsu-set-zenkouho-converted (b zc)
248   (aset (egg-bunsetsu-get-info b) 6 zc))
249
250 (defun canna-get-bunsetsu-source (b)
251   (let ((s (canna-bunsetsu-get-source b)))
252     (or s
253         (let* ((env (canna-bunsetsu-get-env b))
254                (bp (canna-bunsetsu-get-bunsetsu-pos b))
255                (s (cannarpc-get-bunsetsu-source env bp)))
256           (canna-bunsetsu-set-source b s)))))
257 (defun canna-get-bunsetsu-converted (b) (canna-bunsetsu-get-converted b))
258 (defun canna-get-source-language (b) 'Japanese)
259 (defun canna-get-converted-language (b) 'Japanese)
260
261 (defun canna-envspec-create (env-name convert-mode nostudy)
262   (vector (and env-name (setq env-name (intern env-name)))
263           (canna-make-backend egg-language env-name)
264           convert-mode nostudy (list nil)))
265
266 (defsubst canna-envspec-env-type (spec)           (aref spec 0))
267 (defsubst canna-envspec-backend (spec)            (aref spec 1))
268 (defsubst canna-envspec-mode (spec)               (aref spec 2))
269 (defsubst canna-envspec-nostudy (spec)            (aref spec 3))
270 (defsubst canna-envspec-dic-list (spec)           (cdr (aref spec 4)))
271
272 (defsubst canna-envspec-add-dic-list (spec &rest dic)
273   (nconc (aref spec 4) (list (apply 'vector dic))))
274
275 (defmacro canna-arg-type-error (func)
276   `(egg-error ,(format "%s: Wrong type argument" func)))
277
278 (defun canna-define-environment (&optional env-name convert-mode nostudy)
279   "Define a Canna environment. ENV-NAME specifies suffix of the Canna
280 environment name. CONVERT-MODE specifies including hiragana or
281 katakana to candidates list. NOSTUDY specifies not study."
282   (if (and env-name (null (stringp env-name)))
283       (canna-arg-type-error canna-define-environment))
284   (setq canna-current-envspec (canna-envspec-create env-name
285                                                     convert-mode nostudy)
286         canna-envspec-list (nconc canna-envspec-list
287                                   (list canna-current-envspec))))
288
289 (defun canna-add-dict (dict dict-rw)
290   (canna-envspec-add-dic-list canna-current-envspec dict dict-rw))
291
292 (defun canna-comm-sentinel (proc reason)        ; assume it is close
293   (let ((inhibit-quit t))
294     (kill-buffer (process-buffer proc))
295     ;; delete env from the list.
296     (setq canna-environments
297           (delq nil (mapcar (lambda (env)
298                               (if (null (eq (cannaenv-get-proc env) proc))
299                                   env))
300                             canna-environments)))))
301
302 (defun canna-open (hostname-list)
303   "Establish the connection to CANNA server.  Return environment object."
304   (let* ((save-inhibit-quit inhibit-quit)
305          (inhibit-quit t)
306          (proc-name "CANNA")
307          (msg-form "Canna: connecting to %S at %s...")
308          (user-name (or canna-user-name (user-login-name)))
309          (id (shell-command-to-string "id"))
310          (group (or canna-group-name
311                     (if (string-match "gid=[0-9]+(\\([^)]+\\))" id)
312                         (match-string 1 id)
313                       "user")))
314          buf hostname port proc result msg)
315     (unwind-protect
316         (progn
317           (setq buf (generate-new-buffer " *CANNA*"))
318     (save-excursion
319       (set-buffer buf)
320       (erase-buffer)
321       (buffer-disable-undo)
322             (set-buffer-multibyte nil)
323             (setq egg-fixed-euc 'fixed-euc-jp))
324           (or (consp hostname-list)
325               (setq hostname-list (list hostname-list)))
326           (while (and hostname-list (null proc))
327             (setq hostname (or (car hostname-list) "")
328                   hostname-list (cdr hostname-list))
329             (if (null (string-match ":" hostname))
330                 (setq port canna-server-port)
331               (setq port (substring hostname (match-end 0))
332                     hostname (substring hostname 0 (match-beginning 0))))
333             (if (and (stringp port) (string-match "^[0-9]+$" port))
334                 (setq port (string-to-int port)))
335             (and (equal hostname "")
336                  (setq hostname (or (getenv "CANNAHOST") "localhost")))
337             (let ((inhibit-quit save-inhibit-quit))
338               (if (and msg
339                        (null (y-or-n-p (format "%s failed. Try to %s? "
340                                                msg hostname))))
341                   (egg-error "abort connect")))
342             (setq msg (format "Canna: connecting to %s..." hostname))
343             (message "%s" msg)
344             (let ((inhibit-quit save-inhibit-quit))
345               (condition-case nil
346                   (setq proc (open-network-stream proc-name buf hostname port))
347                 ((error quit))))
348             (when proc
349               (process-kill-without-query proc)
350               (set-process-coding-system proc 'binary 'binary)
351               (set-process-sentinel proc 'canna-comm-sentinel)
352               (set-marker-insertion-type (process-mark proc) t)
353               (setq result (cannarpc-open proc user-name)) ;; result is context
354               (if (= result -1)
355                   (progn
356           (delete-process proc)
357                     (setq proc nil))
358                 (cannarpc-notice-group-name proc result group)
359                 (cannarpc-set-app-name proc result "EGG4"))))
360           (cons proc result))
361       (if proc
362           (message (concat msg "done"))
363         (if buf (kill-buffer buf))
364         (egg-error 'canna-connect-error)))))
365
366 (defun canna-filename (p)
367   ""
368   (cond ((consp p) (concat (car p) "/" (user-login-name)))
369         (t p)))
370
371 (defun canna-search-environment (backend)
372   (let ((env-list canna-environments)
373         env)
374     (while (and (null env) env-list)
375       (setq env (and (eq (cannaenv-get-backend (car env-list)) backend)
376                      (car env-list))
377             env-list (cdr env-list)))
378     env))
379
380 (defun canna-get-environment (backend)
381   "Return the backend of CANNA environment."
382   (let ((env (canna-search-environment backend))
383         proc context error)
384     (or env
385         (unwind-protect
386             (let* ((language (canna-backend-get-language backend))
387                    specs)
388               (setq proc (canna-open canna-hostname)
389                     context (cdr proc)
390                     proc (car proc)
391                     canna-envspec-list nil)
392               (condition-case err
393                   (egg-load-startup-file 'canna language)
394                 (egg-error
395                  (setq error err)
396                  (signal (car error) (cdr error))))
397               (setq specs canna-envspec-list)
398               (while specs
399                 (canna-create-environment proc context (car specs))
400                 (setq context nil)
401                 (setq specs (cdr specs)))
402               (setq env (canna-search-environment backend)))
403           (when (and proc (null env))
404             (cannarpc-close proc)
405             (if error
406                 (signal (car error) (cdr error))
407               (egg-error 'canna-fail-make-env)))
408             ))))
409
410 (defun canna-create-environment (proc context spec)
411   (let* ((save-inhibit-quit inhibit-quit)
412          (inhibit-quit t)
413          (backend (canna-envspec-backend spec))
414          (convert-mode (canna-envspec-mode spec))
415          (nostudy (canna-envspec-nostudy spec))
416          (dic-list (canna-envspec-dic-list spec))
417          env)
418     (condition-case err
419         (progn
420           (if (not context)
421               (setq context (cannarpc-create-context proc)))
422           (if (< context 0)
423               (egg-error "%s" (cannarpc-get-error-message (- context))))
424           (setq env (cannaenv-create proc context backend convert-mode nostudy))
425           (let ((inhibit-quit save-inhibit-quit))
426             (while dic-list
427               (canna-set-dictionary env (car dic-list))
428               (setq dic-list (cdr dic-list))))
429           (setq canna-environments (nconc canna-environments (list env))))
430       ((egg-error quit)
431        (if (eq (car err) 'egg-error)
432            (message "%s" (nth 1 err)))
433        (if env
434            (progn
435              (cannarpc-close-context env)
436              (setq canna-environments (delq env canna-environments))))
437        (if (eq (car err) 'quit)
438            (signal 'quit (cdr err)))))))
439
440 (defun canna-set-dictionary (env dic-spec)
441   (let ((dname (aref dic-spec 0))
442         (drw   (aref dic-spec 1))
443         did result)
444     (if (= 0 (canna-open-dictionary env dname drw))
445         (cannaenv-add-dic-list env dname drw))))
446
447 (defun canna-open-dictionary (env name rw)
448   (let ((trying t)
449         ret)
450     (while trying
451       (setq ret (cannarpc-open-dictionary env name 0)) ; XXX MODE=0
452       (if (= ret 0)
453           (setq trying nil)
454         (message (egg-get-message 'canna-dict-missing-1) name)
455         (if rw
456         (if (and (y-or-n-p
457                       (format (egg-get-message 'canna-dict-missing-2) name))
458                  (= (cannarpc-make-dictionary env name) 0))
459                 (message (egg-get-message 'canna-dict-created) name)
460               (message "%s" (cannarpc-get-error-message (- ret))))
461           (setq trying nil))))
462     ret))
463
464 (defun canna-save-dictionaries (env)
465   (let ((dic-list (canna-list-writable-dictionaries-byname env))
466         dic)
467     (while dic-list
468       (setq dic (car dic-list)
469             dic-list (cdr dic-list))
470       (cannarpc-save-dictionary env dic))))
471
472 (defun canna-init ()
473   )
474
475 (defun canna-set-converted-yomi (bunsetsu-pos bunsetsu-list)
476   (let ((bl bunsetsu-list)
477         (i bunsetsu-pos)
478         b)
479     (while bl
480       (setq b (car bl))
481       (canna-bunsetsu-set-source b (cannarpc-get-bunsetsu-source env i))
482       (setq i (1+ i)
483             bl (cdr bl)))
484     bunsetsu-list))
485
486 (defun canna-start-conversion (backend yomi &optional context)
487   "Convert YOMI string to kanji, and enter conversion mode.
488 Return the list of bunsetsu."
489   (let* ((env (canna-get-environment backend))
490          (bunsetsu-list (cannarpc-begin-conversion env yomi)))
491     (if (numberp bunsetsu-list) ; XXX error \e$B$N=hM}E,Ev\e(B
492         (progn
493           (if (= -1 (cannarpc-cancel-conversion env))
494               (progn
495           (setq env (canna-get-environment backend))
496                 (canna-finalize-backend)))
497           (setq bunsetsu-list (cannarpc-begin-conversion env yomi))))
498     (canna-set-converted-yomi 0 bunsetsu-list)))
499
500 (defun canna-end-conversion (bunsetsu-list abort)
501   (let* ((env (canna-bunsetsu-get-env (car bunsetsu-list)))
502          (l bunsetsu-list)
503          (len (length bunsetsu-list))
504          (zenkouho-pos-vector (make-vector (* 2 len) 0))
505          (i 0)
506          (mode (if (cannaenv-get-nostudy env) 0 1)) ; MODE=1 \e$B3X=,\e(B  0 \e$B$7$J$$\e(B
507          bunsetsu zenkouho-pos)
508     (if abort
509         (setq mode 0))
510     (while l
511       (setq bunsetsu (car l))
512       (setq l (cdr l))
513       (setq zenkouho-pos (canna-bunsetsu-get-zenkouho-pos bunsetsu))
514       (if (null zenkouho-pos)
515           () ; XXX: NIL--> 0 atteru???
516         (aset zenkouho-pos-vector i 0)  ; XXX Don't support >=256
517         (aset zenkouho-pos-vector (1+ i) zenkouho-pos))
518       (setq i (+ i 2)))
519     (cannarpc-end-conversion env len zenkouho-pos-vector mode)))
520
521 (defun canna-list-candidates (bunsetsu prev-b next-b major)
522   (setq bunsetsu (car bunsetsu))
523   (if (canna-bunsetsu-get-zenkouho bunsetsu)
524       (cons (canna-bunsetsu-get-zenkouho-pos bunsetsu)
525             (canna-bunsetsu-get-zenkouho-converted bunsetsu))
526     (let* ((env (canna-bunsetsu-get-env bunsetsu))
527            (yomi (canna-get-bunsetsu-source bunsetsu))
528            (bunsetsu-pos (canna-bunsetsu-get-bunsetsu-pos bunsetsu))
529            (z (cannarpc-get-bunsetsu-candidates env bunsetsu-pos yomi)))
530       (canna-bunsetsu-set-zenkouho bunsetsu z)
531       (cons (canna-bunsetsu-set-zenkouho-pos bunsetsu 0)
532             (canna-bunsetsu-set-zenkouho-converted
533              bunsetsu
534              (mapcar 'canna-bunsetsu-get-converted z))))))
535
536 ;;; XXX not use ?
537 (defun canna-get-number-of-candidates (bunsetsu)
538   (let ((l (canna-bunsetsu-get-zenkouho bunsetsu)))
539     (if l
540         (length l)
541       nil)))
542
543 (defun canna-decide-candidate (bunsetsu pos prev-b next-b)
544   (let* ((head (car bunsetsu))
545          (candidate-list (canna-bunsetsu-get-zenkouho head))
546          (candidate (nth pos candidate-list)))
547     (canna-bunsetsu-set-zenkouho candidate candidate-list)
548     (canna-bunsetsu-set-zenkouho-pos candidate pos)
549     (canna-bunsetsu-set-zenkouho-converted
550      candidate (canna-bunsetsu-get-zenkouho-converted head))
551     (list (list candidate))))
552
553 (defun canna-special-candidate (bunsetsu prev-b next-b major type)
554   (let* ((head (car bunsetsu))
555          (env (canna-bunsetsu-get-env head))
556          (backend (egg-bunsetsu-get-backend head))
557          (lang (get backend 'language))
558          source converted zenkouho-list kouho-list pos)
559     (when (and (eq lang (get backend 'source-language))
560                (eq lang (get backend 'converted-language)))
561       (cond ((eq lang 'Japanese)
562              (setq source (canna-get-bunsetsu-source head))
563              (cond ((eq type 'egg-hiragana)
564                     (setq converted source))
565                    ((eq type 'egg-katakana)
566                     (setq converted (japanese-katakana source))))
567              (setq zenkouho-list
568                    (cdr (canna-list-candidates bunsetsu prev-b next-b major)))
569              (setq pos
570                    (when (setq kouho-list (member converted zenkouho-list))
571                      (- (length zenkouho-list) (length kouho-list))))))
572       (when pos
573         (canna-decide-candidate bunsetsu pos prev-b next-b)))))
574
575 ;;; XXX not used ?
576 (defun canna-get-current-candidate-number (bunsetsu)
577   (canna-bunsetsu-get-zenkouho-pos bunsetsu))
578
579 ;;; XXX not used ?
580 (defun canna-get-all-candidates (bunsetsu)
581   (let* ((l (canna-bunsetsu-get-zenkouho bunsetsu))
582          (result (cons nil nil))
583          (r result))
584     (catch 'break
585       (while t
586         (let ((candidate (car l)))
587           (setcar r (canna-bunsetsu-get-converted candidate))
588           (if (null (setq l (cdr l)))
589               (throw 'break nil)
590             (setq r (setcdr r (cons nil nil)))))))
591     result))
592
593 (defun canna-change-bunsetsu-length (bunsetsu prev-b next-b len major)
594   (let* ((env (canna-bunsetsu-get-env (car bunsetsu)))
595          (yomi (canna-get-bunsetsu-source (car bunsetsu)))
596          (yomi-length (cond ((< (length yomi) len) -1)
597                             ((> (length yomi) len) -2)
598                             (t nil)))
599          (bunsetsu-pos (canna-bunsetsu-get-bunsetsu-pos (car bunsetsu)))
600          new)
601     (if yomi-length
602         (setq new (canna-set-converted-yomi
603                    bunsetsu-pos
604                    (cannarpc-set-kugiri-changed env yomi-length bunsetsu-pos)))
605       (setq new bunsetsu))
606     (list (list (car new)) prev-b (cdr new))))
607
608 (defun canna-finalize-backend (&optional action)
609   (let* ((save-inhibit-quit inhibit-quit)
610          (inhibit-quit t)
611          (env-list canna-environments)
612          env proc-list saved)
613     (while env-list
614       (setq env (car env-list)
615             env-list (cdr env-list))
616       (condition-case err
617           (progn
618             (unless (memq (cannaenv-get-proc env) proc-list)
619               (setq proc-list (cons (cannaenv-get-proc env) proc-list)))
620             (unless (eq action 'disconnect-only)
621               (unless saved
622                 (setq saved t)
623                 (message (egg-get-message 'canna-dict-saving) "Canna"))
624               (let ((inhibit-quit save-inhibit-quit))
625                 (canna-save-dictionaries env)))
626             (unless (eq action 'save-only)
627               (cannarpc-close-context env)))
628         ((error quit)
629          (message "signal %S occured when dictionary saving" err))))
630     (if saved
631         (message (egg-get-message 'canna-dict-saved) "Canna"))
632     (unless (eq action 'save-only)
633       (while proc-list
634         (if (and (car proc-list)
635                  (eq (process-status (car proc-list)) 'open))
636             (cannarpc-close (car proc-list)))
637         (setq proc-list (cdr proc-list)))))
638   (setq canna-environments nil))
639
640 ;;; word registration
641
642 (defun canna-list-writable-dictionaries-byname (env)
643   (let ((dic-list (cannaenv-get-dic-list env)))
644     (delq nil
645           (mapcar (lambda (dic)
646                     (let ((dname (aref dic 0))
647                           (drw   (aref dic 1)))
648                       (and drw dname)))
649                   dic-list))))
650
651 (defun canna-dictionary-select (env)
652   (let ((dic-list (canna-list-writable-dictionaries-byname env)))
653     (if (= 1 (length dic-list))
654         (car dic-list)
655       (menudiag-select (list 'menu
656                              (egg-get-message 'canna-register-1)
657                              dic-list)))))
658
659 (defun canna-hinshi-MEISHI (kanji yomi)
660   (if (y-or-n-p (concat "\e$B!V\e(B" kanji "\e$B$J!W$O@5$7$$$G$9$+!#\e(B")) "#T15" "#T35"))
661
662 (defun canna-hinshi-SAHEN-MEISHI (kanji yomi)
663   (if (y-or-n-p (concat "\e$B!V\e(B" kanji "\e$B$J!W$O@5$7$$$G$9$+!#\e(B")) "#T10" "#T30"))
664
665 (defmacro canna-hinshi-DOUSHI-check-gobi ()
666   '(progn
667      (setq i 0)
668      (while (> 9 i)
669        (if (string-match (concat (substring gobi i (1+ i)) "$") kanji)
670            (progn
671              (setq renyou  (substring re-gobi i (1+ i)))
672              (setq mizen   (substring mi-gobi i (1+ i)))
673              (setq kanji-gobi   (substring kanji (match-beginning 0)))
674              (setq kanji-gokan (substring kanji 0 (match-beginning 0)))
675              (setq ret (nth i hinshi))
676              (setq i 9)))
677        (setq i (1+ i)))
678      (setq i 0)
679      (while (> 9 i)
680        (if (string-match (concat (substring gobi i (1+ i)) "$") yomi)
681            (progn
682              (setq yomi-gobi  (substring yomi (match-beginning 0)))
683              (setq yomi-gokan (substring yomi 0 (match-beginning 0)))
684              (setq i 9)))
685        (setq i (1+ i)))))
686
687 (defun canna-hinshi-DOUSHI (kanji yomi)
688   (let ((gobi    "\e$B$/$0$9$D$L$V$`$k$&\e(B")
689         (re-gobi "\e$B$-$.$7$A$K$S$_$j$$\e(B")
690         (mi-gobi "\e$B$+$,$5$?$J$P$^$i$o\e(B")
691         (hinshi (list "#K5" "#G5" "#S5" "#T5" "#N5" "#B5" "#M5" "#R5" "#W5"))
692         kanji-gokan yomi-gokan kanji-gobi yomi-gobi mizen renyou
693         i ret1 ret2 ret)
694     (canna-hinshi-DOUSHI-check-gobi)
695     (if (not (and (> (length kanji) 1) (> (length yomi) 1)
696                   (and kanji-gobi yomi-gobi (equal kanji-gobi yomi-gobi))))
697         (if (and kanji-gobi yomi-gobi)
698             (egg-error "\e$BFI$_$H8uJd$N3hMQ$,0c$$$^$9!#F~NO$7$J$*$7$F$/$@$5$$!#\e(B")
699           (egg-error "\e$BFI$_$H8uJd$r=*;_7A$GF~NO$7$F$/$@$5$$!#\e(B")))
700     (cond ((and (> (length kanji) 2) (> (length yomi) 2)
701                 (string-match "\e$B$/$k\e(B$" kanji) (string-match "\e$B$/$k\e(B$" yomi))
702            (setq ret "#KX")
703            (setq kanji-gokan (substring kanji 0 (- (length kanji) 2)))
704            (setq yomi-gokan  (substring yomi  0 (- (length  yomi) 2))))
705           ((and (> (length kanji) 3) (> (length yomi) 3)
706                 (string-match "\e$B$s$:$k\e(B$" kanji) (string-match "\e$B$s$:$k\e(B$" yomi))
707            (setq ret "#NZX")
708            (setq kanji-gokan (substring kanji 0 (- (length kanji) 3)))
709            (setq yomi-gokan  (substring yomi  0 (- (length  yomi) 3))))
710           ((and (> (length kanji) 2) (> (length yomi) 2)
711                 (string-match "\e$B$:$k\e(B$" kanji) (string-match "\e$B$:$k\e(B$" yomi))
712            (setq ret "#ZX")
713            (setq kanji-gokan (substring kanji 0 (- (length kanji) 2)))
714            (setq yomi-gokan  (substring yomi  0 (- (length  yomi) 2))))
715           ((and (> (length kanji) 2) (> (length yomi) 2)
716                 (string-match "\e$B$9$k\e(B$" kanji) (string-match "\e$B$9$k\e(B$" yomi))
717            (setq ret "#SX")
718            (setq kanji-gokan (substring kanji 0 (- (length kanji) 2)))
719            (setq yomi-gokan  (substring yomi  0 (- (length  yomi) 2)))))
720     (if (not (string-match "5$" ret))
721         (if (y-or-n-p (concat "\e$B!X\e(B" kanji "\e$B!Y$r\e(B (" (canna-hinshi-name ret)
722                               ") \e$B$H$7$FEPO?$7$^$9$+\e(B? "))
723             (setq ret (list kanji-gokan yomi-gokan ret))
724           (setq ret "#R5")
725           (setq kanji-gokan (substring kanji 0 (- (length kanji) 1)))
726           (setq yomi-gokan  (substring yomi  0 (- (length  yomi) 1)))))
727     (if (listp ret)
728         ret
729       (if (y-or-n-p "\e$B$5$i$K:Y$+$$IJ;lJ,$1$N$?$a$N<ALd$r$7$F$bNI$$$G$9$+\e(B? ")
730           (progn
731             (setq ret1 (y-or-n-p (concat "\e$B!V\e(B" kanji-gokan mizen
732                                          "\e$B$J$$!W$O@5$7$$$G$9$+!#\e(B")))
733             (setq i 0)
734             (if (eq "#R5" ret)
735                 (while (> 9 i)
736                   (if (string-match (concat (substring re-gobi i (1+ i)) "$")
737                                     kanji-gokan)
738                       (progn (setq renyou nil)
739                              (setq i 9)))
740                   (setq i (1+ i))))
741             (setq ret2 (y-or-n-p (concat "\e$B!V\e(B" kanji-gokan renyou
742                                          "\e$B$,$$$$!W$O@5$7$$$G$9$+!#\e(B")))
743             (setq ret (if ret1 (if ret2 (concat ret "r") ret)
744                         (if ret2 "#KSr" "#KS")))))
745       (list kanji-gokan yomi-gokan ret))))
746
747 (defun canna-hinshi-KEIYOUSHI (kanji yomi)
748   (let (ret)
749     (if (not (and (> (length kanji) 1) (> (length yomi) 1)
750                   (string-match "\e$B$$\e(B$" yomi) (string-match "\e$B$$\e(B$" kanji)))
751         (egg-error "\e$BFI$_$H8uJd$r\e(B \e$B=*;_7A$GF~NO$7$F$/$@$5$$!#Nc\e(B) \e$BAa$$\e(B"))
752     (setq kanji (substring kanji 0 (1- (length kanji))))
753     (setq yomi (substring yomi 0 (1- (length yomi))))
754     (setq ret
755           (if (y-or-n-p "\e$B$5$i$K:Y$+$$IJ;lJ,$1$N$?$a$N<ALd$r$7$F$bNI$$$G$9$+\e(B? ")
756               (if (y-or-n-p (concat "\e$B!V\e(B" kanji "\e$B!W$O@5$7$$$G$9$+!#\e(B"))
757                   "#KYT" "#KY")
758             "#KY"))
759     (list kanji yomi ret)))
760
761 (defun canna-hinshi-KEIYOUDOUSHI (kanji yomi)
762   (let (ret1 ret2 ret)
763     (if (not (and (> (length kanji) 1) (> (length yomi) 1)
764                   (string-match "\e$B$@\e(B$" yomi) (string-match "\e$B$@\e(B$" kanji)))
765         (egg-error "\e$BFI$_$H8uJd$r\e(B \e$B=*;_7A$GF~NO$7$F$/$@$5$$!#Nc\e(B) \e$B@E$+$@\e(B"))
766     (setq kanji (substring kanji 0 (1- (length kanji))))
767     (setq yomi (substring yomi 0 (1- (length yomi))))
768     (setq ret
769           (if (y-or-n-p "\e$B$5$i$K:Y$+$$IJ;lJ,$1$N$?$a$N<ALd$r$7$F$bNI$$$G$9$+\e(B? ")
770               (progn
771                 (setq ret1 (y-or-n-p
772                             (concat "\e$B!V\e(B" kanji "\e$B$9$k!W$O@5$7$$$G$9$+!#\e(B")))
773                 (setq ret2 (y-or-n-p
774                             (concat "\e$B!V\e(B" kanji "\e$B$,$"$k!W$O@5$7$$$G$9$+!#\e(B")))
775                 (if ret1 (if ret2 "#T10" "#T13") (if ret2 "#T15" "#T18")))
776             "#T05"))
777     (list kanji yomi ret)))
778
779 (defun canna-hinshi-FUKUSHI (kanji yomi)
780   (let (ret1 ret2)
781     (if (y-or-n-p "\e$B$5$i$K:Y$+$$IJ;lJ,$1$N$?$a$N<ALd$r$7$F$bNI$$$G$9$+\e(B? ")
782         (progn
783           (setq ret1 (y-or-n-p (concat "\e$B!V\e(B" kanji "\e$B$9$k!W$O@5$7$$$G$9$+!#\e(B")))
784           (setq ret2 (y-or-n-p (concat "\e$B!V\e(B" kanji "\e$B$H!W$O@5$7$$$G$9$+!#\e(B")))
785           (if ret1 (if ret2 "#F04" "#F12") (if ret2 "#F06" "#F14")))
786       "#F14")))
787
788 (defun canna-hinshi-select (kanji yomi)
789   (let ((key (menudiag-select (list 'menu
790                                     (egg-get-message 'canna-register-2)
791                                     canna-hinshi-menu))))
792     (cond ((symbolp key) (funcall
793                           (intern (concat "canna-hinshi-" (symbol-name key)))
794                           kanji yomi))
795           ((stringp key) (cdr (assoc key canna-hinshi-alist))))))
796
797 (defun canna-word-registration (backend kanji yomi)
798   "Register a word KANJI with a pronunciation YOMI."
799   (if (or (null (eq (egg-get-language 0 kanji)
800                     (canna-get-converted-language backend)))
801           (next-single-property-change 0 'egg-lang kanji)
802           (null (eq (egg-get-language 0 yomi)
803                     (canna-get-source-language backend)))
804           (next-single-property-change 0 'egg-lang yomi))
805       (egg-error "word registration: invalid character")
806     (let* ((env (canna-get-environment backend))
807            (dic (canna-dictionary-select env))
808            (hinshi-id (canna-hinshi-select kanji yomi))
809            result)
810       (if (listp hinshi-id)
811           (progn (setq kanji     (car hinshi-id))
812                  (setq yomi      (nth 1 hinshi-id))
813                  (setq hinshi-id (nth 2 hinshi-id))))
814       (setq result (cannarpc-add-word env dic yomi kanji hinshi-id))
815       (if (>= result 0)
816           (progn
817             (cannarpc-save-dictionary env dic)
818             (list (canna-hinshi-name hinshi-id) dic))
819         (egg-error (cannarpc-get-error-message (- result)))))))
820
821 ;;; word delete registration
822
823 (defun canna-word-delete-regist (backend yomi)
824   "Delete a word KANJI from dictionary."
825   (if (= (length yomi) 0)
826       (egg-error "Canna word delete registration: null string"))
827   (let* ((env (canna-get-environment backend))
828          (dic (canna-dictionary-select env))
829          proc context envd bunsetsu bunsetsu-pos z zpos kouho-list hinshi i
830          kanji lex result)
831     (setq proc (cannaenv-get-proc env))
832     (setq context (cannarpc-create-context proc))
833     (setq envd (cannaenv-create proc context
834                                 'canna-backend-Japanese-tmp-delete-regist
835                                 1 t))
836     (canna-set-dictionary envd (vector dic t))
837     (canna-set-dictionary envd (vector "fuzokugo" nil))
838     (setq bunsetsu (car (cannarpc-begin-conversion envd yomi)))
839     (setq bunsetsu-pos (canna-bunsetsu-get-bunsetsu-pos bunsetsu))
840     (setq z (cannarpc-get-bunsetsu-candidates envd bunsetsu-pos yomi))
841     (canna-bunsetsu-set-zenkouho bunsetsu z)
842     (canna-bunsetsu-set-zenkouho-pos bunsetsu 0)
843     (setq kouho-list
844           (canna-bunsetsu-set-zenkouho-converted
845            bunsetsu
846            (mapcar 'canna-bunsetsu-get-converted z)))
847     (setq yomi  (car (last kouho-list)))
848     (setq kouho-list (cdr (reverse kouho-list)))
849     (setq kouho-list (reverse kouho-list))
850     (setq i 0)
851     (setq kouho-list (mapcar '(lambda (k)
852                                 (prog1
853                                     (cons k i)
854                                   (setq i (1+ i))))
855                              kouho-list))
856     (let ((hiragana (assoc yomi kouho-list))
857           hinshi)
858       (if hiragana
859           (setq hinshi (cannarpc-get-hinshi envd bunsetsu-pos (cdr hiragana))))
860       (if (stringp hinshi)
861           (if (equal "#T35" hinshi)
862               (setq kouho-list (delete hiragana kouho-list)))
863         (setq kouho-list (delete hiragana kouho-list))))
864     (cond
865      ((null kouho-list)
866       (cannarpc-close-context envd)
867       (egg-error "\e$BEPO?$5$l$F$$$^$;$s!#\e(B"))
868      ((eq 1 (length kouho-list))
869       (setq zpos 0)
870       (setq kanji (car (car kouho-list))))
871      (t
872       (setq kanji (menudiag-select (list 'menu "\e$B:o=|\e(B:" kouho-list) nil nil t))
873       (setq zpos (cdr (car kanji)))
874       (setq kanji (car (car kanji)))))
875     (setq hinshi (cannarpc-get-hinshi envd bunsetsu-pos zpos))
876     (setq lex (cannarpc-get-lex envd bunsetsu-pos zpos))
877     (cannarpc-cancel-conversion envd)
878     (if (string-match "#[^#]+" hinshi)
879         (setq hinshi (substring hinshi 0 (match-end 0)))
880       (egg-error "\e$BIJ;l>pJs$,<hF@$G$-$^$;$s!#\e(B"))
881     (setq kanji (substring kanji 0 (nth 1 (car lex))))
882     (setq yomi (substring yomi 0 (car (car lex))))
883     (if (y-or-n-p (concat "\e$B!X\e(B" kanji "\e$B!Y\e(B(" yomi ": "
884                           (canna-hinshi-name hinshi) ")\e$B$r\e(B "
885                           dic " \e$B$+$i:o=|$7$^$9$+\e(B? "))
886         (setq result
887               (cannarpc-delete-word envd dic yomi kanji hinshi))
888       (setq result -1))
889     (if (>= result 0)
890         (progn
891           (cannarpc-save-dictionary envd dic)
892           (cannarpc-close-context envd)
893           (list kanji yomi (canna-hinshi-name hinshi) dic))
894       (cannarpc-close-context envd)
895       (egg-error "\e$B:o=|$5$l$^$;$s$G$7$?!#\e(B"))
896     ))
897
898 ;;; setup
899 (load "egg/cannarpc")
900 (run-hooks 'canna-load-hook)
901
902 ;;;###autoload
903 (defun egg-activate-canna (&rest arg)
904   "Activate CANNA backend of Tamago 4."
905   (apply 'egg-mode (append arg canna-backend-alist)))
906
907 ;;; egg/canna.el ends here.