Sync up with egg-980712.
[elisp/egg.git] / egg / wnn.el
1 ;;; egg/wnn.el --- WNN Support (high level interface) in Egg
2 ;;;                Input Method Architecture
3
4 ;; Copyright (C) 1997, 1998 Mule Project, Powered by Electrotechnical
5 ;; Laboratory, JAPAN.
6 ;; Project Leader: Satoru Tomura <tomura@etl.go.jp>
7
8 ;; Author: NIIBE Yutaka <gniibe@mri.co.jp>
9 ;;         KATAYAMA Yoshio <kate@pfu.co.jp> ; Korean, Chinese support.
10 ;;
11 ;; Maintainer: NIIBE Yutaka <gniibe@mri.co.jp>
12
13 ;; This file is part of EGG.
14
15 ;; EGG is free software; you can redistribute it and/or modify
16 ;; it under the terms of the GNU General Public License as published by
17 ;; the Free Software Foundation; either version 2, or (at your option)
18 ;; any later version.
19
20 ;; EGG is distributed in the hope that it will be useful,
21 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
22 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 ;; GNU General Public License for more details.
24
25 ;; You should have received a copy of the GNU General Public License
26 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
27 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
28 ;; Boston, MA 02111-1307, USA.
29
30 ;;; Commentary:
31
32 ;;; Code:
33
34 (require 'egg-edep)
35
36 (defgroup wnn nil
37   "Wnn interface for Tamagotchy"
38   :group 'egg)
39
40 (defconst wnn-support-languages '(Japanese Chinese-GB Chinese-CNS Korean))
41
42 (eval-when-compile
43   (defmacro WNN-const (c)
44     (cond ((eq c 'BUN_SENTOU) -1)
45           ((eq c 'NO_EXIST)  1)
46           ((eq c 'NO_MATCH) 10)
47           ((eq c 'IMA_OFF)   -4)
48           ((eq c 'IMA_ON)    -3)
49           ((eq c 'HINDO_NOP) -2)
50           ((eq c 'HINDO_INC) -3))))
51
52 (defconst wnn-conversion-backend
53   [ wnn-init
54
55     wnn-start-conversion
56       wnn-get-bunsetsu-converted
57       wnn-get-bunsetsu-source
58       wnn-list-candidates
59           wnn-get-number-of-candidates
60           wnn-get-current-candidate-number
61           wnn-get-all-candidates
62           wnn-decide-candidate
63       wnn-change-bunsetsu-length
64     wnn-end-conversion
65     wnn-start-reverse-conversion
66
67     wnn-fini
68  ])
69
70 ;; <env> ::= [ <proc> <env-id> <server-type> <dic-set> <rev-flag>
71 ;;             <daibunsetsu-info> ]
72
73 (defsubst wnnenv-create (proc env-id server-type dic-set rev-flag)
74   (vector proc env-id server-type dic-set rev-flag nil))
75
76 (defsubst wnnenv-get-proc (env)
77   (aref env 0))
78
79 (defsubst wnnenv-get-env-id (env)
80   (aref env 1))
81
82 (defsubst wnnenv-get-server-type (env)
83   (aref env 2))
84
85 (defsubst wnnenv-get-dictionary-set (env)
86   (aref env 3))
87
88 (defsubst wnnenv-get-reverse-flag (env)
89   (aref env 4))
90
91 (defsubst wnnenv-get-daibunsetsu-info (env)
92   (aref env 5))
93 (defsubst wnnenv-set-daibunsetsu-info (env d)
94   (aset env 5 d))
95
96 ;; <bunsetsu> ::= [ <env> <end> <start> <jiritsugo-end> <dic-no>
97 ;;                  <entry> <freq> <right-now> <hinshi> <status>
98 ;;                  <status-backward> <kangovect> <evaluation>
99 ;;
100 ;;                  <converted> <yomi> <fuzokugo>
101 ;;                  <zenkouho> <freq-down>
102 ;;                  <zenkouho-pos> ]
103 ;;
104 (defsubst wnn-bunsetsu-create (e end start jiritsugo-end dic-no entry freq
105                                right-now hinshi status status-backward
106                                kangovect evaluation)
107   (vector e end start jiritsugo-end dic-no entry freq right-now
108           hinshi status status-backward kangovect evaluation
109           nil nil nil nil nil nil))
110
111 (defsubst wnn-bunsetsu-get-env (bunsetsu)
112   (aref bunsetsu 0))
113
114 (defsubst wnn-bunsetsu-get-converted (bunsetsu)
115   (aref bunsetsu 13))
116 (defsubst wnn-bunsetsu-set-converted (bunsetsu converted)
117   (aset bunsetsu 13 converted))
118
119 (defsubst wnn-bunsetsu-get-hinshi (bunsetsu)
120   (aref bunsetsu 8))
121
122 (defsubst wnn-bunsetsu-get-dic-no (bunsetsu)
123   (aref bunsetsu 4))
124
125 (defsubst wnn-bunsetsu-get-entry (bunsetsu)
126   (aref bunsetsu 5))
127
128 (defsubst wnn-bunsetsu-get-right-now (bunsetsu)
129   (aref bunsetsu 7))
130
131 (defsubst wnn-bunsetsu-get-yomi (bunsetsu)
132   (aref bunsetsu 14))
133 (defsubst wnn-bunsetsu-set-yomi (bunsetsu yomi)
134   (aset bunsetsu 14 yomi))
135
136 (defsubst wnn-bunsetsu-get-fuzokugo (bunsetsu)
137   (aref bunsetsu 15))
138 (defsubst wnn-bunsetsu-set-fuzokugo (bunsetsu fuzokugo)
139   (aset bunsetsu 15 fuzokugo))
140
141 (defsubst wnn-bunsetsu-get-zenkouho (bunsetsu)
142   (aref bunsetsu 16))
143 (defsubst wnn-bunsetsu-set-zenkouho (bunsetsu z)
144   (aset bunsetsu 16 z))
145
146 (defsubst wnn-bunsetsu-get-freq-down (bunsetsu)
147   (aref bunsetsu 17))
148 (defsubst wnn-bunsetsu-set-freq-down (bunsetsu d)
149   (aset bunsetsu 17 d))
150
151 (defsubst wnn-bunsetsu-get-zenkouho-pos (bunsetsu)
152   (aref bunsetsu 18))
153 (defsubst wnn-bunsetsu-set-zenkouho-pos (bunsetsu zp)
154   (aset bunsetsu 18 zp))
155 \f
156 (defvar wnn-environments nil
157   "Environment for WNN kana-kanji conversion")
158
159 (defcustom wnn-jserver nil "jserver host list" :group 'wnn :type 'string)
160 (defcustom wnn-cserver nil "cserver host list" :group 'wnn :type 'string)
161 (defcustom wnn-tserver nil "tserver host list" :group 'wnn :type 'string)
162 (defcustom wnn-kserver nil "kserver host list" :group 'wnn :type 'string)
163
164 (defcustom wnn-jport 22273 "jserver port number" :group 'wnn :type 'integer)
165 (defcustom wnn-cport 22289 "cserver port number" :group 'wnn :type 'integer)
166 (defcustom wnn-tport 22321 "tserver port number" :group 'wnn :type 'integer)
167 (defcustom wnn-kport 22305 "kserver port number" :group 'wnn :type 'integer)
168
169 ;; The port number should be initialized from $WNNLIB/serverdefs by wnn-init
170 (defconst wnn-server-info-list
171   ;; language    locale  server  port     stream coding-system hostname
172   '((Japanese    "ja_JP" jserver wnn-jport "Wnn"  fixed-euc-jp wnn-jserver)
173     (Chinese-GB  "zh_CN" cserver wnn-cport "cWnn" fixed-euc-cn wnn-cserver)
174     (Chinese-CNS "zh_TW" tserver wnn-tport "tWnn" fixed-euc-tw wnn-tserver)
175     (Korean      "ko_KR" kserver wnn-kport "kWnn" fixed-euc-kr wnn-kserver)))
176
177 (defsubst wnn-server-get-info (lang)
178   (assq (or lang its-current-language) wnn-server-info-list))
179
180 (defsubst wnn-server-language (info)
181   (car info))
182
183 (defsubst wnn-server-locale (info)
184   (nth 1 info))
185
186 (defsubst wnn-server-type (info)
187   (nth 2 info))
188
189 (defsubst wnn-server-port (info)
190   (symbol-value (nth 3 info)))
191
192 (defsubst wnn-server-stream-name (info)
193   (nth 4 info))
194
195 (defsubst wnn-server-buffer-name (info)
196   (concat " *" (wnn-server-stream-name info) "*"))
197
198 (defsubst wnn-server-coding-system (info)
199   (nth 5 info))
200
201 (defsubst wnn-server-hostname (info)
202   (symbol-value (nth 6 info)))
203
204 (defun wnn-init ()
205   )
206
207 (defun wnn-start-conversion (yomi &optional language dic-set reverse)
208   "Convert YOMI string to kanji, and enter conversion mode.
209 Return the list of bunsetsu."
210   (let ((server-info (wnn-server-get-info language)))
211     (if server-info
212         (let* ((env (wnn-get-environment server-info dic-set reverse))
213                (result (wnnrpc-renbunsetsu-conversion env yomi
214                                                       (WNN-const BUN_SENTOU) "")))
215           (wnnenv-set-daibunsetsu-info env (car result))
216           (cdr result))
217     (signal 'lang-not-supported))))
218
219 (defun wnn-start-reverse-conversion (yomi &optional language dic-set)
220   (wnn-start-conversion yomi language dic-set t))
221
222 (defun wnn-get-bunsetsu-converted (bunsetsu)
223   (concat (wnn-bunsetsu-get-converted bunsetsu)
224           (wnn-bunsetsu-get-fuzokugo  bunsetsu)))
225
226 ;; WNN-UNIQ-CANDIDATES
227 ;;
228 ;; Here, IMNSHO, WNN is broken.
229 ;; WNN must/should return unique one.  The word is representative
230 ;; among possible words with same string literal.
231 ;;
232 ;; With no bunsetsu information to users, users have to chose
233 ;; the word based on the string literal only.
234 ;; How we could update frequency?
235 ;;
236 ;; We'll modify WNN in future.
237 ;;
238 ;; 
239 (defun wnn-uniq-candidates (bunsetsu bunsetsu-list)
240   (let ((hash-table (make-vector 31 0)) ; XXX why 31?
241         (l bunsetsu-list)
242         (i 0)
243         (n 0) sym0 result p b sym)
244     (setq sym0 (intern (wnn-get-bunsetsu-converted bunsetsu) hash-table))
245     (while l
246       (setq b (car l)
247             l (cdr l)
248             sym (intern (wnn-get-bunsetsu-converted b) hash-table))
249       (if (null (boundp sym))           ; new one
250           (let ((bl (cons b nil)))
251             (set sym b)
252             (if (eq sym0 sym)
253                 (wnn-bunsetsu-set-zenkouho-pos bunsetsu (setq n i)))
254             (if p
255                 (setq p (setcdr p bl))
256               (setq result (setq p bl)))
257             (setq i (1+ i)))))
258     (wnn-bunsetsu-set-zenkouho bunsetsu result)
259     n))
260
261 (defun wnn-list-candidates (bunsetsu prev-bunsetsu)
262   (let* ((candidates (wnn-bunsetsu-get-zenkouho bunsetsu))
263          (yomi (concat (wnn-bunsetsu-get-yomi bunsetsu)
264                        (wnn-bunsetsu-get-fuzokugo bunsetsu)))
265          (converted (concat (wnn-bunsetsu-get-converted bunsetsu)
266                             (wnn-bunsetsu-get-fuzokugo bunsetsu)))
267          (env (wnn-bunsetsu-get-env bunsetsu))
268          prev-hinshi
269          prev-fuzokugo)
270     (if candidates
271         ;; We have the candidates already.  Return the current position.
272         (wnn-bunsetsu-get-zenkouho-pos bunsetsu)
273       (if (null prev-bunsetsu)
274           (setq prev-hinshi -1
275                 prev-fuzokugo "")
276         (setq prev-hinshi (wnn-bunsetsu-get-hinshi prev-bunsetsu)
277               prev-fuzokugo (wnn-bunsetsu-get-fuzokugo prev-bunsetsu)))
278       (setq candidates
279             (wnnrpc-get-bunsetsu-candidates env yomi
280                                             prev-hinshi prev-fuzokugo))
281       (wnn-uniq-candidates bunsetsu candidates))))
282
283 (defun wnn-get-number-of-candidates (bunsetsu)
284   (let ((l (wnn-bunsetsu-get-zenkouho bunsetsu)))
285     (if l
286         (length l)
287       nil)))
288
289 (defun wnn-get-current-candidate-number (bunsetsu)
290   (wnn-bunsetsu-get-zenkouho-pos bunsetsu))
291
292 (defun wnn-get-all-candidates (bunsetsu)
293   (let* ((l (wnn-bunsetsu-get-zenkouho bunsetsu))
294          (result (cons nil nil))
295          (r result))
296     (catch 'break
297       (while t
298         (let ((candidate (car l)))
299           (setcar r (concat (wnn-bunsetsu-get-converted candidate)
300                             (wnn-bunsetsu-get-fuzokugo candidate)))
301           (if (null (setq l (cdr l)))
302               (throw 'break nil)
303             (setq r (setcdr r (cons nil nil)))))))
304     result))
305
306 (defun wnn-decide-candidate (bunsetsu candidate-pos)
307   (let* ((candidate-list (wnn-bunsetsu-get-zenkouho bunsetsu))
308          (candidate (nth candidate-pos candidate-list)))
309     (wnn-bunsetsu-set-zenkouho candidate candidate-list)
310     (wnn-bunsetsu-set-zenkouho-pos candidate candidate-pos)
311     candidate))
312
313 ;;
314 ;;
315 (defun wnn-change-bunsetsu-length (b0 b1 b2 len)
316   (let ((yomi (concat
317                (wnn-get-bunsetsu-source b1)
318                (if b2 (wnn-get-bunsetsu-source b2))))
319         (env (wnn-bunsetsu-get-env b1))
320         yomi1 yomi2 prev-hinshi prev-fuzokugo
321         bunsetsu1 bunsetsu2)
322     (if (null b0)
323         (setq prev-hinshi -1
324               prev-fuzokugo "")
325       (setq prev-hinshi (wnn-bunsetsu-get-hinshi b0)
326             prev-fuzokugo (wnn-bunsetsu-get-fuzokugo b0)))
327     (setq yomi1 (substring yomi 0 len)
328           yomi2 (substring yomi len))
329     (setq bunsetsu1
330           (car (wnnrpc-tanbunsetsu-conversion env yomi1
331                                               prev-hinshi prev-fuzokugo)))
332     ;; Only set once.
333     (wnn-bunsetsu-set-freq-down bunsetsu1
334                                 (or (wnn-bunsetsu-get-freq-down b1)
335                                     (if b2
336                                         (list b1 b2)
337                                       (list b1))))
338     (if (< 0 (length yomi2))
339         (setq bunsetsu2
340               (cdr (wnnrpc-renbunsetsu-conversion
341                     env yomi2
342                     (wnn-bunsetsu-get-hinshi bunsetsu1)
343                     (wnn-bunsetsu-get-fuzokugo bunsetsu1))))
344       (setq bunsetsu2 nil))
345     (if bunsetsu2
346         (append (list bunsetsu1) bunsetsu2)
347       (list bunsetsu1))))
348
349
350 (defun wnn-get-bunsetsu-source (bunsetsu)
351   (concat (wnn-bunsetsu-get-yomi bunsetsu)
352           (wnn-bunsetsu-get-fuzokugo bunsetsu)))
353
354 (defun wnn-end-conversion (bunsetsu-info-list abort)
355   (if abort
356       ()
357     (let ((env (wnn-bunsetsu-get-env (car bunsetsu-info-list))))
358       (wnn-update-frequency env bunsetsu-info-list)
359       (wnnenv-set-daibunsetsu-info env nil))))
360
361 (defvar wnn-sticky-environment-flag nil
362   "*Flag which specifies sticky environment.")
363
364 (defmacro wnn-for-each-environment (lang env &rest body)
365   `(let* ((server-info (wnn-server-get-info ,lang))
366          (server-type (wnn-server-type server-info))
367          (env-list wnn-environments))
368     (if server-type
369         (while env-list
370           (let ((,env (car env-list)))
371             ,@body)
372           (setq env-list (cdr env-list))))))
373
374 (defun wnn-fini ()
375   (let ((l wnn-support-languages))
376     (while l
377       (wnn-fini-lang (car l))
378       (setq l (cdr l)))))
379
380 (defun wnn-fini-lang (lang &optional save-only)
381   (let* ((server-info (wnn-server-get-info lang))
382          (server-type (wnn-server-type server-info))
383          (l wnn-environments)
384          new-env-list del-list env proc)
385     (if server-type
386         (progn
387           (message "%s \e$B$NIQEY>pJs!&<-=q>pJs$rB`Hr$7$F$$$^$9\e(B"
388                    (wnn-server-stream-name server-info))
389           (while l
390             (setq env (car l))
391             (if (eq (wnnenv-get-server-type env) server-type)
392                 (condition-case nil
393                     (progn
394                       (wnn-save-dictionaries env)
395                       (if (null save-only)
396                           (progn
397                             (setq del-list (nconc del-list (list env)))
398                             (if wnn-sticky-environment-flag
399                                 (wnnrpc-make-env-sticky env)
400                               (wnnrpc-make-env-unsticky env))
401                             (wnnrpc-disconnect env))))
402                   (error nil))
403               (setq new-env-list (nconc new-env-list (list env))))
404             (setq l (cdr l)))
405           (message "%s \e$B$NIQEY>pJs!&<-=q>pJs$rB`Hr$7$^$7$?\e(B"
406                    (wnn-server-stream-name server-info))
407           (if (null save-only)
408               (progn
409                 (setq proc (and del-list (wnnenv-get-proc (car del-list))))
410                 (if (and proc (eq (process-status proc) 'open))
411                     (progn
412                       (wnnrpc-close proc)
413                       (kill-buffer (process-buffer proc))))
414                 (setq wnn-environments new-env-list)))))))
415
416 (defun wnn-close (lang)
417   (interactive (list (wnn-read-active-lang)))
418   (or (listp lang)
419       (setq lang (list lang)))
420   (while lang
421     (wnn-fini-lang (car lang))
422     (setq lang (cdr lang))))
423
424 (defun wnn-dictionary-save (lang)
425   (interactive (list (wnn-read-active-lang)))
426   (or (listp lang)
427       (setq lang (list lang)))
428   (while lang
429     (wnn-fini-lang (car lang) t)
430     (setq lang (cdr lang))))
431
432 (defun wnn-read-active-lang ()
433   (let ((completion-ignore-case t)
434         (env wnn-environments)
435         langs server server-list)
436     (while env
437       (setq server (wnnenv-get-server-type (car env))
438             env (cdr env))
439       (if (null (member server server-list))
440           (setq server-list (cons server server-list))))
441     (setq langs (delete nil
442                         (mapcar (lambda (info)
443                                   (if (memq (wnn-server-type info) server-list)
444                                       (wnn-server-language info)))
445                                 wnn-server-info-list)))
446     (if (<= (length langs) 1)
447         langs
448       (setq langs (cons (cons "All" langs)
449                         (mapcar (lambda (lang) (cons (symbol-name lang) lang))
450                                 langs)))
451       (cdr (assoc (completing-read "language? " langs nil t) langs)))))
452 \f
453 ;;
454 (defun wnn-comm-sentinel (proc reason)  ; assume it is close
455                                         ; tamago-971009 version
456   (let ((l wnn-environments)
457         env l1)
458     (kill-buffer (process-buffer proc))
459     ;; delete env from the list.
460     (while l
461       (setq env (car l))
462       (if (eq proc (wnnenv-get-proc env))
463           (progn
464             (if l1
465                 (setcdr l1 (cdr l))
466               (setq wnn-environments (cdr l)))
467             (setq l (cdr l)))
468         (setq l1 l
469               l (cdr l))))))
470
471 ;;
472 (defvar wnn-open-message)
473
474 (defun wnn-open (server-info)
475   "Establish the connection to WNN server.  Return process object."
476   ;; Open the session to WNN server, 
477   (let ((buf (generate-new-buffer (wnn-server-buffer-name server-info)))
478         (server-type (wnn-server-type server-info))
479         (hostname-list (wnn-server-hostname server-info))
480         (msg-form "WNN: connecting to %S at %s...")
481         hostname proc result msg)
482     (save-excursion
483       (set-buffer buf)
484       (erase-buffer)
485       (buffer-disable-undo)
486       (setq egg-fixed-euc (wnn-server-coding-system server-info))
487       (set-buffer-multibyte nil))
488     (cond
489      ((null hostname-list)
490       (setq hostname-list '("localhost")))
491      ((null (listp hostname-list))
492       (setq hostname-list (list hostname-list))))
493     (while (and hostname-list (null proc))
494       (setq hostname (car hostname-list)
495             hostname-list (cdr hostname-list))
496       (message msg-form server-type hostname)
497       (condition-case result
498           (setq proc (open-network-stream (wnn-server-stream-name server-info)
499                                           buf
500                                           hostname
501                                           (wnn-server-port server-info)))
502         (error nil))
503       (if proc
504           (progn
505             (process-kill-without-query proc)
506             (set-process-coding-system proc 'no-conversion 'no-conversion)
507             (set-process-sentinel proc 'wnn-comm-sentinel)
508             (set-marker-insertion-type (process-mark proc) t)
509             (setq result (wnnrpc-open proc
510                                       (if (equal hostname "localhost")
511                                           "unix"
512                                         (system-name))
513                                       (user-login-name)))
514             (if (< result 0)
515                 (progn
516                   (delete-process proc)
517                   (setq proc nil
518                         msg (format "Can't open WNN session (%s %S): %s"
519                                     hostname
520                                     (wnn-server-type server-info)
521                                     msg)))))))
522     (if proc
523         (progn
524           (setq wnn-open-message (format (concat msg-form "done")
525                                          server-type
526                                          hostname))
527           proc)
528       (kill-buffer buf)
529       (error "%s" (or msg (format "no %S available" server-type))))))
530
531 (defvar wnn-dictionary-specification-list
532   '((jserver
533      (nil nil ""
534           [2 10 2 45 100 200 5 1 40 -100 200 -100 200 80 200 200 200]
535           "pubdic/full.fzk"
536           ["pubdic/kihon.dic"     ("kihon.h")    5 nil t]
537           ["pubdic/setsuji.dic"   ("setsuji.h")  5 nil t]
538           ["pubdic/koyuu.dic"     ("koyuu.h")    1 nil t]
539           ["pubdic/chimei.dic"    ("chimei.h")   1 nil t]
540           ["pubdic/jinmei.dic"    ("jinmei.h")   1 nil t]
541           ["pubdic/special.dic"   ("special.h")  5 nil t]
542           ["pubdic/computer.dic"  ("computer.h") 5 nil t]
543           ["pubdic/symbol.dic"    ("symbol.h")   1 nil t]
544           ["pubdic/tankan.dic"    nil            1 nil nil]
545           ["pubdic/bio.dic"       ("bio.h")      1 nil t]
546 ;         ["gerodic/g-jinmei.dic" ("g-jinmei.h") 1 nil t]
547           ["wnncons/tankan2.dic"  nil            1 nil nil]
548           ["wnncons/tankan3.dic"  nil            1 nil nil]
549           [("ud")                 nil            5 t   t])
550      (nil t "R"
551           [2 10 2 45 1 80 5 1 50 -20 400 -10 100 -100 200 0 200]
552           "pubdic/full.fzk"
553           ["pubdic/kihon.dic"     ("kihon.h")    5 nil t]
554           ["pubdic/setsuji.dic"   ("setsuji.h")  5 nil t]
555           ["pubdic/koyuu.dic"     ("koyuu.h")    1 nil t]
556           ["pubdic/chimei.dic"    ("chimei.h")   1 nil t]
557           ["pubdic/jinmei.dic"    ("jinmei.h")   1 nil t]
558           ["pubdic/special.dic"   ("special.h")  5 nil t]
559           ["pubdic/computer.dic"  ("computer.h") 5 nil t]
560           ["pubdic/symbol.dic"    ("symbol.h")   1 nil t]
561           ["pubdic/tankan.dic"    nil            1 nil nil]
562           ["pubdic/bio.dic"       ("bio.h")      1 nil t]
563 ;         ["gerodic/g-jinmei.dic" ("g-jinmei.h") 1 nil t]
564           ["wnncons/tankan2.dic"  nil            1 nil nil]
565           ["wnncons/tankan3.dic"  nil            1 nil nil]
566           [("ud")                 nil            5 t   t]))
567     (cserver
568      (Q   nil "Q"
569           nil
570           "sys/full.con"
571           ["sys/QianMa.dic"       nil            1 nil nil])
572      (W   nil "W"
573           nil
574           "sys/full.con"
575           ["sys/WuBi.dic"         nil            1 nil nil])
576      (nil nil "PZ"
577           [1 5 2 750 10 80 10 5 1000 50 0 -200 0 0 0 16 0]
578           "sys/full.con"
579           ["sys/level_1.dic"      ("level_1.h")  4 nil t]
580           ["sys/level_2.dic"      ("level_2.h")  1 nil t]
581           ["sys/basic.dic"        ("basic.h")    7 nil t]
582           ["sys/computer.dic"     ("computer.h") 4 nil t]
583           ["sys/cwnn.dic"         ("cwnn.h")     4 nil t]
584           [("ud")                 nil            5 t   t])
585      (Q   t "QR"
586           nil
587           "sys/full.conR"
588           ["sys/QianMa.dic"       nil            1 nil nil])
589      (W   t "WR"
590           nil
591           "sys/full.conR"
592           ["sys/WuBi.dic"         nil            1 nil nil])
593      (nil t "PZR"
594           [1 5 2 750 10 80 10 5 1000 50 0 -200 0 0 0 16 0]
595           "sys/full.conR"
596           ["sys/level_1.dic"      ("level_1.h")  4 nil t]
597           ["sys/level_2.dic"      ("level_2.h")  1 nil t]
598           ["sys/basic.dic"        ("basic.h")    7 nil t]
599           ["sys/computer.dic"     ("computer.h") 4 nil t]
600           ["sys/cwnn.dic"         ("cwnn.h")     4 nil t]
601           [("ud")                 nil            5 t   t]))
602     (tserver
603      (nil nil ""
604           [1 5 2 750 10 80 10 5 1000 50 0 -200 0 0 0 16 0]
605           "sys/full.con"
606           ["sys/cns_ch.dic"       ("cns_ch.h")   4 nil t]
607           ["sys/cns_wd.dic"       ("cns_wd.h")   1 nil t]
608           [("ud")                 nil            5 t   t])
609      (nil t "R"
610           [1 5 2 750 10 80 10 5 1000 50 0 -200 0 0 0 16 0]
611           "sys/full.conR"
612           ["sys/cns_ch.dic"       ("cns_ch.h")   4 nil t]
613           ["sys/cns_wd.dic"       ("cns_wd.h")   1 nil t]
614           [("ud")                 nil            5 t   t]))
615     (kserver
616      (nil nil ""
617           [2 5 2 45 200 80 5 1 40 0 400 -100 400 80 200 2 200]
618           "sys/full.fzk"
619           ["sys/hword.dic"        ("hword.h")    5 nil t]
620           ["sys/single.dic"       ("single.h")   1 nil t]
621           [("ud")                 nil            2 t   t])
622      (nil t "R"
623           [2 10 2 45 1 80 5 1 50 -20 400 -10 100 -100 200 0 200]
624           "sys/full.fzk"
625           ["sys/hword.dic"        ("hword.h")    5 nil t]
626           ["sys/single.dic"       ("single.h")   1 nil t]
627           [("ud")                 nil            2 t   t]))))
628
629 (defsubst wnn-get-dic-spec (server)
630   (cdr (assoc server wnn-dictionary-specification-list)))
631
632 (defsubst wnn-dic-spec-dic-set (spec)
633   (nth 0 spec))
634
635 (defsubst wnn-dic-spec-reverse (spec)
636   (nth 1 spec))
637
638 (defsubst wnn-dic-spec-name (spec)
639   (nth 2 spec))
640
641 (defsubst wnn-dic-spec-param (spec)
642   (nth 3 spec))
643
644 (defsubst wnn-dic-spec-fuzokugo (spec)
645   (nth 4 spec))
646
647 (defsubst wnn-dic-spec-dic-list (spec)
648   (nthcdr 5 spec))
649
650
651 (defcustom wnn-usr-dic-dir (concat "usr/" (user-login-name))
652   "*Directory of user dictionary for Wnn."
653   :group 'wnn
654   :type 'string)
655
656 (defun wnn-filename (p)
657   ""
658   (cond ((consp p) (concat wnn-usr-dic-dir "/" (car p)))
659         (t p)))
660
661 (defun wnn-open-file (proc env-id filename)
662   "Open the file FILENAME on the environment ENV-ID on server process PROC.
663 Return file descripter.  NIL means NO-file.
664 On failure, return negate-encoded error code."
665   (if filename
666       (wnnrpc-open-file proc env-id filename)
667     nil))
668
669 (defun wnn-create-directory (proc env-id path)
670   "Create directory to the path."
671   (let ((dir (directory-file-name path))
672         create-list)
673     (while (and dir (/= (wnnrpc-access proc env-id 0 dir) 0))
674       (setq create-list (cons dir create-list)
675             dir (file-name-directory dir))
676       (if dir
677           (setq dir (directory-file-name dir))))
678     (if (null create-list)
679         t                               ; Already exist.
680       ;; Only query once.
681       (if (y-or-n-p (format "\e$B%G%#%l%/%H%j\e(B(%s)\e$B$,M-$j$^$;$s!#:n$j$^$9$+\e(B? " path))
682           (catch 'return
683             (while create-list
684               (let* ((dir (car create-list))
685                      (ret (wnnrpc-mkdir proc env-id dir)))
686                 (if (< ret 0)
687                     (progn
688                       (message "\e$B%G%#%l%/%H%j\e(B(%s)\e$B$N:n@.$K<:GT$7$^$7$?\e(B" dir)
689                       (throw 'return nil))))
690               (setq create-list (cdr create-list)))
691             ;; Success
692             (message "\e$B%G%#%l%/%H%j\e(B(%s)\e$B$r:n$j$^$7$?\e(B" path)
693             t)
694         ;; Failure
695         nil))))
696
697 (defun wnn-open-dictionary (proc env-id dicname mode)
698   (let ((dictionary (wnn-open-file proc env-id dicname)))
699     (if (null dictionary)
700         (throw 'wnn-set-dictionary-tag nil)
701       (while (< dictionary 0)
702         (let ((err-code (- dictionary)))
703           (if (or (null mode) (/= err-code (WNN-const NO_EXIST)))
704               (let ((msg (wnnrpc-get-error-message err-code)))
705                 (message "\e$B<-=q%U%!%$%k\e(B(%s)\e$B$,$"$j$^$;$s\e(B: %s" dicname msg)
706                 (throw 'wnn-set-dictionary-tag nil)) ; Failure
707             ;; Try to create new one
708             (if (and (y-or-n-p
709                       (format "\e$B<-=q%U%!%$%k\e(B(%s)\e$B$,$"$j$^$;$s!#:n$j$^$9$+\e(B? "
710                               dicname))
711                      (wnn-create-directory proc env-id
712                                            (file-name-directory dicname))
713                      (= (wnnrpc-create-dictionary proc env-id dicname) 0))
714                 (progn
715                   (message "\e$B<-=q%U%!%$%k\e(B(%s)\e$B$r:n$j$^$7$?\e(B" dicname)
716                   (setq dictionary
717                         (wnnrpc-open-file proc env-id dicname)))
718               (throw 'wnn-set-dictionary-tag nil)))))
719       dictionary)))
720
721 (defun wnn-open-frequency (proc env-id freqname mode dic)
722   (let ((frequency (wnn-open-file proc env-id freqname)))
723     (if (null frequency)
724         (setq frequency -1)
725       (while (< frequency 0)
726         (let ((err-code (- frequency)))
727           (if (or (null mode) (/= err-code (WNN-const NO_EXIST)))
728               (let ((msg (wnnrpc-get-error-message err-code)))
729                 (message "\e$BIQEY%U%!%$%k\e(B(%s)\e$B$,$"$j$^$;$s\e(B: %s" freqname msg)
730                 (throw 'wnn-set-dictionary-tag nil)) ; Failure
731             ;; Try to create new one
732             (if (and (y-or-n-p
733                       (format "\e$BIQEY%U%!%$%k\e(B(%s)\e$B$,$"$j$^$;$s!#:n$j$^$9$+\e(B? "
734                               freqname))
735                      (wnn-create-directory proc env-id
736                                            (file-name-directory freqname))
737                      (= (wnnrpc-create-frequency proc env-id freqname dic) 0))
738                 (progn
739                   (message "\e$BIQEY%U%!%$%k\e(B(%s)\e$B$r:n$j$^$7$?\e(B" freqname)
740                   (setq frequency
741                         (wnnrpc-open-file proc env-id freqname)))
742               (throw 'wnn-set-dictionary-tag nil))))))
743       frequency))
744
745 ;; Using local file (uploading/downloading) is not supported yet.
746 ;; Password is not supported (Password is questionable feature, anyway)
747 (defun wnn-set-dictionary (proc env-id reverse-flag dic-spec)
748   ""
749   (catch 'wnn-set-dictionary-tag
750     (let ((dicname (wnn-filename (aref dic-spec 0)))
751           (freqname (wnn-filename (aref dic-spec 1)))
752           (priority  (aref dic-spec 2))
753           (dic-mode  (aref dic-spec 3))
754           (freq-mode (aref dic-spec 4))
755           dictionary frequency)
756       (setq dictionary (wnn-open-dictionary proc env-id dicname dic-mode))
757       (setq frequency
758             (wnn-open-frequency proc env-id freqname freq-mode dictionary))
759       (wnn-set-dictionary-sub proc env-id reverse-flag
760                               dictionary frequency priority dic-mode
761                               freq-mode dicname freqname))))
762
763 (defun wnn-set-dictionary-sub (proc env-id reverse-flag
764                                dictionary frequency priority dic-mode
765                                freq-mode dicname freqname)
766   (let ((trying t))
767     (while trying
768       (let ((ret (wnnrpc-set-dictionary proc env-id reverse-flag
769                                         dictionary frequency
770                                         priority dic-mode freq-mode)))
771         (if (< ret 0)
772             (let ((err-code (- ret)))
773               (if (or (null freq-mode) (/= err-code (WNN-const NO_MATCH)))
774                   (let ((msg (wnnrpc-get-error-message (- ret))))
775                     (message "WNN: Error on setting dictionary (%s, %s): %s"
776                              dicname freqname msg)
777                     (setq trying nil))  ; done
778                 ;; No-match: Create new frequency and try it again
779                 (wnnrpc-discard-file proc env-id frequency) ; XXX: error?
780                 (setq frequency
781                       (wnn-query-del/create-frequency proc env-id freqname
782                                                       dictionary))))
783           ;; done sucessfully
784           (setq trying nil))))))
785
786 (defun wnn-query-del/create-frequency (proc env-id freqname dictionary)
787   (if (y-or-n-p
788        (format "\e$B<-=q$HIQEY\e(B(%s)\e$B$N@09g@-$,$"$j$^$;$s!#:n$jD>$7$^$9$+\e(B? "
789                freqname))
790       (progn
791         (wnnrpc-remove-file proc freqname) ; XXX: error?
792         (wnnrpc-create-frequency proc env-id freqname dictionary) ; XXX: error?
793         (message "\e$BIQEY%U%!%$%k\e(B(%s)\e$B$r:n$j$^$7$?\e(B" freqname)
794         (wnnrpc-open-file proc env-id freqname)) ; XXX: error?
795     -1))
796
797 (defun wnn-get-environment (server-info &optional dic-set reverse)
798   "Return WNN Environemt for the conversion server specified
799 by SERVER-INFO.  If none, create new environment.  Optional
800 argument DIC-SET specifies dictionary set.  Optional argument
801 REVERSE specifies reverse conversion, if non nil."
802   (let ((server-type (wnn-server-type server-info))
803         (env wnn-environments)
804         proc spec e s)
805     (setq reverse (null (null reverse)))
806     (if (catch 'found
807           (while env
808             (setq e (car env))
809             (if (and (eq (wnnenv-get-server-type e) server-type)
810                      (eq (wnnenv-get-dictionary-set e) dic-set)
811                      (eq (wnnenv-get-reverse-flag e) reverse))
812                 (throw 'found t))
813             (setq env (cdr env))))
814         e
815       (setq proc (wnn-open server-info)
816             spec (wnn-get-dic-spec server-type))
817       (while spec
818         (setq s (car spec)
819               e (wnn-create-environment proc server-type s)
820               wnn-environments (cons e wnn-environments))
821         (if (and (eq (wnn-dic-spec-dic-set s) dic-set)
822                  (eq (wnn-dic-spec-reverse s) reverse))
823             (setq env e))
824         (setq spec (cdr spec)))
825       (if (null env)
826           (error "WNN: environment for %S%s (%s) not found"
827                  server-type
828                  (if dic-set (format "(%S)" dic-set) "")
829                  (if reverse 'reverse 'normal)))
830       (message "%s" wnn-open-message)
831       env)))
832
833 (defun wnn-create-environment (proc server-type spec)
834   ""
835   ;; Create new data structure: something like wnn_buf
836   ;; Process, Environment-ID and Daibunsetsu-info.
837   (let (env-id parameters filename fuzokugo-fid ret dic-set reverse)
838     (setq env-id (wnnrpc-connect proc (wnn-make-env-name spec)))
839     (if (< env-id 0)
840         (error "Can't connect new WNN environment: %s"
841                (wnnrpc-get-error-message (- env-id))))
842     (setq dic-set (wnn-dic-spec-dic-set spec)
843           reverse (wnn-dic-spec-reverse spec)
844           parameters (wnn-dic-spec-param spec)
845           filename (wnn-filename (wnn-dic-spec-fuzokugo spec))
846           fuzokugo-fid (wnn-open-file proc env-id filename))
847     (if (null fuzokugo-fid)
848         (setq fuzokugo-fid -1)
849       (if (< fuzokugo-fid 0)
850           (progn
851             (message "WNN: Can't open fuzokugo file (%s): %s"
852                      filename
853                      (wnnrpc-get-error-message (- fuzokugo-fid)))
854             (setq fuzokugo-fid -1))))
855     (setq ret (wnnrpc-set-fuzokugo-file proc env-id fuzokugo-fid))
856     (if (< ret 0)
857         (let ((msg (wnnrpc-get-error-message (- ret))))
858           (message "WNN: Error on setting fuzokugo (%s): %s" filename msg)))
859     (setq spec (wnn-dic-spec-dic-list spec))
860     (while spec
861       (wnn-set-dictionary proc env-id reverse (car spec))
862       (setq spec (cdr spec)))
863     (if parameters
864         (wnnrpc-set-conversion-parameters proc env-id parameters))
865     (wnnenv-create proc env-id server-type dic-set reverse)))
866
867 (defvar wnn-user-name nil)
868
869 (defun wnn-make-env-name (spec)
870   (or wnn-user-name
871       (setq wnn-user-name (getenv "WNNUSER"))
872       (setq wnn-user-name (user-login-name)))
873   (concat wnn-user-name (wnn-dic-spec-name spec)))
874
875 (defun wnn-update-frequency (env bunsetsu-info-list)
876   (let ((l bunsetsu-info-list))
877     (while l
878       (let* ((b (car l))
879              (fd (wnn-bunsetsu-get-freq-down b))
880              (z (wnn-bunsetsu-get-zenkouho b)))
881         (while fd
882           (let* ((fdb (car fd))
883                  (dic-no (wnn-bunsetsu-get-dic-no fdb))
884                  (entry (wnn-bunsetsu-get-entry fdb)))
885             (wnnrpc-set-frequency env dic-no entry
886                                   (WNN-const IMA_OFF) (WNN-const HINDO_NOP))
887             (setq fd (cdr fd))))
888         (while z
889           (let* ((zb (car z))
890                  (right-now (wnn-bunsetsu-get-right-now zb))
891                  (dic-no (wnn-bunsetsu-get-dic-no zb))
892                  (entry (wnn-bunsetsu-get-entry zb)))
893             (if (and (/= right-now 0) (/= dic-no -1))
894                 (wnnrpc-set-frequency env dic-no entry (WNN-const IMA_OFF)
895                                       (WNN-const HINDO_NOP)))
896             (setq z (cdr z))))
897         (let ((dic-no (wnn-bunsetsu-get-dic-no b))
898               (entry (wnn-bunsetsu-get-entry b)))
899           (if (/= dic-no -1)
900               (wnnrpc-set-frequency env dic-no entry 
901                                     (WNN-const IMA_ON)
902                                     (WNN-const HINDO_INC))))
903         (setq l (cdr l))))))
904 \f
905 ;;; XXX Need alternative implementation
906 ;(defun wnn-set-conversion-mode ()
907 ;  (jl-set-environment))
908
909 (defsubst wnn-dicinfo-entry (info) (aref info 0))
910 (defsubst wnn-dicinfo-id (info freq) (aref info (+ 1 freq)))
911 (defsubst wnn-dicinfo-mode (info freq) (aref info (+ 3 freq)))
912 (defsubst wnn-dicinfo-enable (info) (aref info 5))
913 (defsubst wnn-dicinfo-nice (info) (aref info 6))
914 (defsubst wnn-dicinfo-reverse (info) (aref info 7))
915 (defsubst wnn-dicinfo-comment (info) (aref info 8))
916 (defsubst wnn-dicinfo-name (info freq) (aref info (+ 9 freq)))
917 (defsubst wnn-dicinfo-passwd (info freq) (aref info (+ 11 freq)))
918 (defsubst wnn-dicinfo-type (info) (aref info 13))
919 (defsubst wnn-dicinfo-words (info) (aref info 14))
920 (defsubst wnn-dicinfo-local (info freq) (aref info (+ 15 freq)))
921
922 (defun wnn-save-dictionaries (env)
923   (let ((dic-list (wnnrpc-get-dictionary-list-with-environment env))
924         (result 0) info freq)
925     (while (and dic-list
926                 (>= result 0))
927       (setq info (car dic-list)
928             dic-list (cdr dic-list)
929             freq 0)
930       (while (<= freq 1)
931         (if (and (> (wnn-dicinfo-id info freq) 0)
932                  (= (wnn-dicinfo-mode info freq) 0))
933             (if (= (wnn-dicinfo-local info freq) 1)
934                 (setq result (wnnrpc-write-file env
935                                                 (wnn-dicinfo-id info freq)
936                                                 (wnn-dicinfo-name info freq)))
937               (message "WNN: remote dictionary (%s) not supported yet"
938                        (wnn-dicinfo-name info freq))
939               (ding)
940               (sit-for 1)))
941         (if (< result 0)
942             (wnnrpc-disconnect env))
943         (setq freq (1+ freq))))))
944
945 (defun wnn-version (proc)
946   "Return version number string of WNN server."
947   (format "%x" (wnnrpc-version proc)))
948
949 (defun wnn-dai-bunsetsu-p ()
950   (jl-dai-top ))
951
952 (defun wnn-next-dai-bunsetsu-pos ()
953   XXX)
954 \f
955 ;;; not implemented yet (NIY)
956 (defun wnn-delete-dictionary ()
957   (dj-delete-dic XXX))
958
959 ;;; NIY, might never be implemented
960 (defun wnn-server-inspect ())
961
962 ;;; NIY
963 (defun wnn-list-dictionaries ()
964   (jl-dic-list))
965
966 ;;; NIY
967 (defun wnn-get-conversion-parameters ()
968   (js-get-parameters))
969
970 ;;; Dictionary management (word registration) is not implemented yet.
971
972 ;; XXX: local file loaded into the server: Not supported yet
973 ;(defun wnn-list-dictionaries (env)
974 ;  (wnnrpc-get-dictionary-list-with-environment env))
975
976 (defun wnn-find-dictionary-by-id (id dic-list)
977   (catch 'return
978     (while dic-list
979       (let ((dic (car dic-list)))
980         (if (= (wnndic-get-id dic) id)
981             (throw 'return dic)
982           (setq dic-list (cdr dic-list)))))))
983
984 (defun wnn-dict-name (dic)
985   (let ((name (wnndic-get-comment dic)))
986     (if (string= name "")
987         (file-name-nondirectory (wnndic-get-dictname dic))
988       name)))
989
990 (defun wnn-list-writable-dictionaries-byname (env)
991   (let ((dic-list (wnnrpc-get-dictionary-list-with-environment env))
992         (w-id-list (wnnrpc-get-writable-dictionary-id-list env)))
993     (mapcar (function (lambda (id)
994                         (let ((dic (wnn-find-dictionary-by-id id dic-list)))
995                           (cons (wnn-dict-name dic) dic))))
996             w-id-list)))
997
998 (defun wnn-hinshi-list (env dic name)
999   (let ((dic-number (wnndic-get-id dic)))
1000     (wnnrpc-get-hinshi-list env dic-number name)))
1001
1002 (defun wnn-hinshi-number (env hinshi-name)
1003   (wnnrpc-hinshi-number (wnnenv-get-proc env) hinshi-name))
1004
1005 (defun wnn-add-word (env dic yomi kanji comment hinshi-id initial-freq)
1006   (let ((dic-number (wnndic-get-id dic)))
1007     (wnnrpc-add-word env dic-number yomi kanji comment
1008                      hinshi-id initial-freq)))
1009
1010 ;;; setup
1011
1012 (require 'egg)
1013
1014 ;;;###autoload
1015 (defun egg-activate-wnn (&rest arg)
1016   "Activate Wnn backend of Tamagotchy."
1017   (setq egg-conversion-backend wnn-conversion-backend)
1018   (if (not (fboundp 'wnnrpc-open))
1019       (load-library "egg/wnnrpc"))
1020   (apply 'egg-mode arg))
1021
1022 ;;; egg/wnn.el ends here.