;; Sj3 server interface for Egg
;; Coded by K.Ishii, Sony Corp. (kiyoji@sm.sony.co.jp)

;; This file is part of Egg on Mule (Multilingual Environment)

;; Egg is distributed in the forms of patches to GNU
;; Emacs under the terms of the GNU EMACS GENERAL PUBLIC
;; LICENSE which is distributed along with GNU Emacs by the
;; Free Software Foundation.

;; Egg is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied
;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
;; PURPOSE.  See the GNU EMACS GENERAL PUBLIC LICENSE for
;; more details.

;; You should have received a copy of the GNU EMACS GENERAL
;; PUBLIC LICENSE along with Nemacs; see the file COPYING.
;; If not, write to the Free Software Foundation, 675 Mass
;; Ave, Cambridge, MA 02139, USA.

;;; 1998-10-02	Ported to Emacs 20.3.90.
;;; 1998-10-10	Revised.

;;;
;;; Mule - Sj3 server interface in elisp
;;;

(provide 'sj3-client)

;;;;  修正メモ；；

;;;   Aug-4-94 by K.Ishii
;;;   Bug fixed in sj3-put-kata.

;;;   Apr-6-94 by N.Tanaka 
;;;   Add version 2(japanese EUC) protocol

;;;   Jun-16-93 by H.Shirasaki <sirasaki@rd.ecip.osaka-u.ac.jp>
;;;   In sj3-bunsetu-yomi-equal, typo fixed.

;;;   Apr-6-93 by T.Saneto <sanewo@pdp.crl.sony.co.jp>
;;;   Bug fixed in sj3-bunsetu-yomi-equal.

;;;   Mar-19-93 by K.Ishii
;;;   Changed sj3-server-dict-info for edit-dict

;;;   Aug-6-92 by K.Ishii
;;;   日本語判定に lc-jp を使うように変更

;;;   Jul-30-92 by K.Ishii
;;;   長い文章を変換するときに起こる "Args out of range" エラーの修正
;;;   登録した動詞の削除ができるように sj3-server-dict-info の修正
;;;   sj3serv に渡すプログラム名の変更

;;;   Jun-2-92 by K.Ishii
;;;   Mule 用に変更

;;;   Dec-12-91 by K.Ishii
;;;   文節学習がうまくできないことがあるため、sj3-result-buffer を変更
;;;
;;;   sj3-get-stdy で "Count exceed." エラーを良く起こすので削除

;;;   Nov-26-91 by K.Ishii
;;;   sj3-server-open で host_name と user_name を渡す順番の修正
;;;
;;;   sj3-server-henkan-next を実行してよく起こる "Count exceed." という
;;;   バグの修正
;;;
;;;   sj3-server-henkan-next で一括変換と文節変換で第一候補が違った場合
;;;   に起こるバグの修正(これに伴い文節学習 sj3-server-b-study の修正)

;;;
;;;  Sj3 deamon command constants
;;;

(defconst SJ3_OPEN          1  "利用者登録")
(defconst SJ3_CLOSE         2  "利用者削除")
;;;
(defconst SJ3_DICADD       11 "辞書追加")
(defconst SJ3_DICDEL       12 "辞書削除")
;;;
(defconst SJ3_OPENSTDY	   21  "学習ファイルオープン")
(defconst SJ3_CLOSESTDY	   22  "学習ファイルクローズ")
(defconst SJ3_STDYSIZE	   23  "")
;;;
(defconst SJ3_LOCK         31 "辞書ロック")
(defconst SJ3_UNLOCK       32 "辞書アンロック")
;;;
(defconst SJ3_BEGIN        41 "変換開始")
(defconst SJ3_BEGIN_EUC   111 "変換開始")
;;;
(defconst SJ3_TANCONV      51 "再変換（文節伸縮）")
(defconst SJ3_TANCONV_EUC 112 "再変換（文節伸縮）")
(defconst SJ3_KOUHO        54 "候補")
(defconst SJ3_KOUHO_EUC   115 "候補")
(defconst SJ3_KOUHOSU      55 "候補数")
(defconst SJ3_KOUHOSU_EUC 116 "候補数")
;;;
(defconst SJ3_STDY         61 "文節学習")
(defconst SJ3_END          62 "文節長学習")
(defconst SJ3_END_EUC     117 "文節長学習")
;;;
(defconst SJ3_WREG         71 "単語登録")
(defconst SJ3_WREG_EUC    118 "単語登録")
(defconst SJ3_WDEL         72 "単語削除")
(defconst SJ3_WDEL_EUC    119 "単語削除")
;;;
(defconst SJ3_MKDIC        81 "")
(defconst SJ3_MKSTDY       82 "")
(defconst SJ3_MKDIR        83 "")
(defconst SJ3_ACCESS       84 "")
;;;
(defconst SJ3_WSCH         91 "単語検索")
(defconst SJ3_WSCH_EUC    120 "単語検索")
(defconst SJ3_WNSCH        92 "")
(defconst SJ3_WNSCH_EUC   121 "")
;;;
(defconst SJ3_VERSION     103 "")

;;;  Sj3 server version error
(defconst SJ3_DifferentVersion 11 "")
(defvar *sj3-current-server-version* nil)

(defvar sj3-server-buffer nil  "Buffer associated with Sj3 server process.")

(defvar sj3-server-process nil  "Sj3 Kana Kanji hankan process.")

(defvar sj3-command-tail-position nil)
(defvar sj3-command-buffer nil)

(defvar sj3-result-buffer nil)
(defvar sj3-henkan-string nil)
(defvar sj3-bunsetu-suu   nil)

(defvar sj3-return-code nil)
(defvar sj3-error-code nil)

(defvar sj3-stdy-size nil)
(defvar sj3-user-dict-list nil)
(defvar sj3-sys-dict-list nil)
(defvar sj3-yomi-llist nil)

;;;
;;;  Macro(s)
;;;

(defmacro sj3-save-buffer-multibyte-and-enable (&rest body)
  "Save multibyte flag; set multibyte flag; execute BODY;
restore multibyte flag. Executes BODY just like `progn'."
  `(let ((flag enable-multibyte-characters))
     (unwind-protect
	 (progn
	   (set-buffer-multibyte default-enable-multibyte-characters)
	   ,@body)
       (set-buffer-multibyte flag))))

;;;
;;;  Put data into buffer 
;;;

(defun sj3-put-4byte (integer)
  (insert (logand 255 (ash integer -24))
	  (logand 255 (ash integer -16))
	  (logand 255 (ash integer -8))
	  (logand 255 (ash integer 0)) ))

(defun sj3-put-string (str)
  (insert str 0))

(defun sj3-put-string* (str)
  (let ((sstr (if (= *sj3-current-server-version* 2)
		  (encode-coding-string str 'euc-japan)
		(encode-coding-string str 'sjis))))
    (insert sstr 0)))

;;;
;;; Get data from buffer
;;;

(defun sj3-get-4byte ()

  (let ((c 0) (point (point)))
    ;;;(goto-char (point-min))
    (while (< (point-max) (+ point 4))
      (accept-process-output)
      (if (= c 10) (if t (progn (sit-for 0) (setq c 0)) (error "Count exceed.")))
      (setq c (1+ c)))
    (goto-char point))

  (let ((point (point)))
    (if (not (or (and (= (char-after point) 0)
		      (< (char-after (+ point 1)) 128))
		 (and (= (char-after point) 255)
		      (<= 128 (char-after (+ point 1))))))
	(error "sj3-get-4byte: integer range overflow."))
    (prog1
	(logior
	 (lsh (- (logxor (char-after point) 128) 128) 24)
	 (lsh (char-after (+ point 1)) 16)
	 (lsh (char-after (+ point 2))  8)
	 (lsh (char-after (+ point 3))  0))
      (goto-char (+ (point) 4)))))

(defun sj3-get-byte ()
  (let ((c 0) (point (point)))
    (while (< (point-max) (1+ point))
      (accept-process-output)
      (if (= c 10) (if t (progn (sit-for 0) (setq c 0)) (error "Count exceed.")))
      (setq c (1+ c)))
    (goto-char point)
    (prog1
	(lsh (char-after point) 0)
      (forward-char 1))))

(defun sj3-get-string ()
  (let ((point (point)))
    (skip-chars-forward "^\0")
    (let ((c 0))
      (while (eobp)
	(accept-process-output)
	(if (= c 10) (if t (progn (sit-for 0) (setq c 0)) (error "Count exceed")))
	(setq c (1+ c))
	(skip-chars-forward "^\0")))
    (prog1 
	(buffer-substring point (point))
      (forward-char 1))))

(defalias 'sj3-get-string* 'sj3-get-convert-string)

(defun sj3-get-convert-string ()
  (let ((point (point)) (c 0) str)
    (while (not (search-forward "\0" nil t))
      (accept-process-output)
      (goto-char point)
      (if (= c 10) (if t (progn (sit-for 0) (setq c 0)) (error "Count exceed")))
      (setq c (1+ c)))
    (setq str (buffer-substring point (1- (point))))
    (if (= *sj3-current-server-version* 2)
	(decode-coding-string str 'euc-japan)
      (decode-coding-string str 'sjis))))

(defun sj3-get-stdy ()
  (let ((c 0) (point (point)))
    (while (< (point-max) (+ point sj3-stdy-size))
      (accept-process-output)
      (if (>= c 10) (progn (sit-for 0) (setq c 0))) ;;; delete error
      (setq c (1+ c)))
    (goto-char (+ point sj3-stdy-size))
    (buffer-substring point (point))))

;;;
;;; Sj3 Server Command Primitives
;;;

(defun sj3-command-start (command)
  (set-buffer sj3-command-buffer)
  (goto-char (point-min))
  (if (not (= (point-max) (+ sj3-command-tail-position 1024)))
      (error "sj3 command start error"))
  (delete-region (point-min) sj3-command-tail-position)
  (sj3-put-4byte command))

(defun sj3-command-reset ()
  (save-excursion
    (set-process-coding-system sj3-server-process 'binary 'binary)
      (set-buffer sj3-command-buffer)
      (buffer-disable-undo sj3-command-buffer)
      (erase-buffer)
      (setq sj3-command-tail-position (point-min))
      (let ((max 1024) (i 0))
	(while (< i max)
	  (insert 0)
	  (setq i (1+ i))))))

(defun sj3-command-end ()
  (set-buffer sj3-server-buffer)
  (erase-buffer)
  (set-buffer sj3-command-buffer)
  (setq sj3-command-tail-position (point))
;;  (process-send-region sj3-server-process (point-min)
;;	       (+ (point-min) (lsh (1+ (lsh (- (point) (point-min)) -10)) 10)))
  (process-send-region sj3-server-process (point-min) (1+ (point)))
  )

;;;
;;; Sj3 Server Reply primitives
;;;

(defun sj3-get-result ()
  (set-buffer sj3-server-buffer)
  (condition-case ()
      (accept-process-output sj3-server-process)
    (error nil))
  (goto-char (point-min)))

(defun sj3-get-return-code ()
  (setq sj3-return-code (sj3-get-4byte))
  (setq sj3-error-code  (if (= sj3-return-code 0) nil
			  (sj3-error-symbol sj3-return-code)))
  (if sj3-error-code nil
    sj3-return-code))

;;;
;;; Sj3 Server Interface:  sj3-server-open
;;;

;(defvar *sj3-server-max-kana-string-length* 1000)
;(defvar *sj3-server-max-bunsetu-suu* 1000)

(defvar *sj3-server-version* 2)
(setq *sj3-server-version* 2)
(defvar *sj3-program-name* "sj3-egg-m")
(defvar *sj3-service-name* "sj3")

(defun sj3-server-open (server-host-name login-name)
  (if (sj3-server-active-p) t
     (let ((server_version *sj3-server-version*)
	   (sj3serv_name 
	   (if (or (null  server-host-name)
		   (equal server-host-name "")
		   (equal server-host-name "unix"))
	       (system-name)
	     server-host-name))
	  (user_name
	   (if (or (null login-name) (equal login-name ""))
	       (user-login-name)
	     login-name))
	  (host_name (system-name))
	  (program_name 
	   (format "%d.%s" 
		  (string-to-int (substring (make-temp-name "") 1 6))
		  *sj3-program-name*)))
       (setq sj3-server-buffer (get-buffer-create " [Sj3 Output Buffer] "))
      (setq sj3-server-process 
	    (condition-case var
		(open-network-stream "Sj3" sj3-server-buffer
				     sj3serv_name *sj3-service-name* )
	      (error 
	        (cond((string-match "Unknown host" (car (cdr var)))
		      (setq sj3-error-code (list ':SJ3_UNKNOWN_HOST
						 sj3serv_name)))
		     ((string-match "Unknown service" (car (cdr var)))
		      (setq sj3-error-code (list ':SJ3_UNKNOWN_SERVICE
						 *sj3-service-name*)))
		     (t ;;; "Host ... not respoding"
		      (setq sj3-error-code ':SJ3_SOCK_OPEN_FAIL)))
		     nil)))
      (if (null sj3-server-process)
	  (progn
	    (kill-buffer sj3-server-buffer)
	    nil)
	(process-kill-without-query sj3-server-process)
	(setq sj3-command-buffer (get-buffer-create " [Sj3 Command Buffer] "))
	(setq sj3-result-buffer (get-buffer-create " [Sj3 Result Buffer] "))

	(save-excursion 
	      (set-process-coding-system 
	       sj3-server-process 'binary 'binary)
	  (progn
	    (set-buffer sj3-server-buffer)
	    (set-buffer-multibyte nil)
	    (buffer-disable-undo sj3-server-buffer)
	    )
	  (progn
	    (set-buffer sj3-result-buffer)
	    (set-buffer-multibyte t)
	    (buffer-disable-undo sj3-result-buffer))
	  (progn  
	    (set-buffer sj3-command-buffer)
	    (set-buffer-multibyte nil)
	    (buffer-disable-undo sj3-command-buffer)
	    (erase-buffer)
	    (setq sj3-command-tail-position (point-min))
	    (let ((max 1024) (i 0))
	      (while (< i max)
		(insert 0)
		(setq i (1+ i)))))
	  (sj3-clear-dict-list)
	  (sj3-command-start SJ3_OPEN)
	  (sj3-put-4byte server_version)
	  (sj3-put-string host_name)
	  (sj3-put-string user_name)
	  (sj3-put-string program_name)
	  (sj3-command-end)
	  (sj3-get-result)
	  (sj3-get-return-code)
	  (if (= sj3-return-code SJ3_DifferentVersion)
	      (progn (sj3-command-start SJ3_OPEN)
	             (sj3-put-4byte 1)
	             (sj3-put-string host_name)
	             (sj3-put-string user_name)
	             (sj3-put-string program_name)
	             (sj3-command-end)
	             (sj3-get-result)
	             (sj3-get-return-code)
	             (if (not (= sj3-return-code 0))
		         (sj3-connection-error)
		       nil)
		     )
	    nil)
	  (if (or (= sj3-return-code 0) (> -1 sj3-return-code))
	      (progn  (setq *sj3-current-server-version* 1)
	              (if (not (= sj3-return-code 0))
	                  (setq *sj3-current-server-version* (- 0 sj3-return-code))
			nil)
                      (sj3-get-stdy-size)
		      )
	    nil)
	  )))))

(defun sj3-server-active-p ()
  (and sj3-server-process
       (eq (process-status sj3-server-process) 'open)))

(defun sj3-connection-error ()
  (setq sj3-error-code ':sj3-no-connection)
  (setq sj3-return-code -1)
  nil)

(defun sj3-zero-arg-command (op)
  (if (sj3-server-active-p)
      (progn
	(sj3-command-start op)
	(sj3-command-end)
	(sj3-get-result)
	(sj3-get-return-code))
    (sj3-connection-error)))

(defun sj3-server-close ()
  (let (dict-no)
    (while (and (sj3-server-active-p) (setq dict-no (car sj3-sys-dict-list)))
      (sj3-server-close-dict dict-no)
      (setq sj3-sys-dict-list (cdr sj3-sys-dict-list)))
    (while (and (sj3-server-active-p) (setq dict-no (car sj3-user-dict-list)))
      (sj3-server-close-dict dict-no)
      (setq sj3-user-dict-list (cdr sj3-user-dict-list)))
    (sj3-clear-dict-list))
  (sj3-server-close-stdy)
  (sj3-zero-arg-command SJ3_CLOSE)
  (if (sj3-server-active-p)
      (delete-process sj3-server-process))
  (if sj3-server-buffer
      (kill-buffer sj3-server-buffer))
  (if sj3-command-buffer
      (kill-buffer sj3-command-buffer))
  (if sj3-result-buffer
      (kill-buffer sj3-result-buffer))
  (setq sj3-server-process nil)
  (setq sj3-server-buffer nil)
  (setq sj3-command-buffer nil)
  (setq sj3-result-buffer nil))

(defun sj3-clear-dict-list ()
  (setq sj3-sys-dict-list nil)
  (setq sj3-user-dict-list nil))

;; Remove Wnn's stuff.
(remove-hook 'kill-emacs-hook 'wnn-egg:kill-emacs-function)

(defun sj3-egg:kill-emacs-function ()
  (if (sj3-server-active-p)
      (sj3-server-close)))

(add-hook 'kill-emacs-hook 'sj3-egg:kill-emacs-function)

(defun sj3-get-stdy-size ()
  (sj3-zero-arg-command SJ3_STDYSIZE)
  (if (not (= sj3-return-code 0)) nil
      (setq sj3-stdy-size (sj3-get-4byte))))

(defun sj3-put-stdy-dmy ()
  (let ((i 0))
    (while (< i sj3-stdy-size)
      (insert 0)
      (setq i (1+ i)))))

;;; Sj3 Result Buffer's layout:
;;;
;;; { length:4  kana 0 kouhoSuu:4 kouhoNo:4
;;;   {studyData kanji 0 } ...
;;; }
;;;   0 0 0 0

(defun sj3-skip-length ()
  (goto-char (+ (point) 4)))

(defun sj3-skip-4byte ()
  (goto-char (+ (point) 4)))

(defun sj3-skip-yomi ()
  (skip-chars-forward "^\0") (forward-char 1))

(defun sj3-skip-stdy ()
  (goto-char (+ (point) sj3-stdy-size)))

;;;
;;; entry function
;;;
(defun sj3-server-henkan-begin (henkan-string)
  (if (not (sj3-server-active-p)) (sj3-connection-error)
    (let ((inhibit-quit t) mb-str)
      (save-excursion
	(setq sj3-henkan-string henkan-string)
	(if (= *sj3-current-server-version* 2)
	    (setq mb-str (encode-coding-string henkan-string 'euc-japan))
	  (setq mb-str (encode-coding-string henkan-string 'sjis)))
	(set-buffer sj3-result-buffer)
	(erase-buffer)
	(setq sj3-bunsetu-suu 0)
	(setq sj3-yomi-llist nil)
	(goto-char (point-min))
	(if (= *sj3-current-server-version* 2)
	    (sj3-command-start SJ3_BEGIN_EUC)
	  (sj3-command-start SJ3_BEGIN)
	)
	(sj3-put-string mb-str)
	(sj3-command-end)
	(sj3-get-result)
	(sj3-get-return-code)
	(if (not (= sj3-return-code 0)) nil
	  (let ((yp 0) p0 yl offset ystr stdk)
	    (sj3-get-4byte)
	    (set-buffer sj3-result-buffer)
	    (delete-region (point) (point-max))
	    (setq p0 (point))
	    (insert sj3-henkan-string 0 0 0 0)
	    (goto-char p0)
	    (set-buffer sj3-server-buffer)
	    (while (> (setq yl (sj3-get-byte)) 0)
	      (setq ystr (substring mb-str yp (+ yp yl))
		    yp (+ yp yl))
	      (sj3-save-buffer-multibyte-and-enable
	       (if (= *sj3-current-server-version* 2)
		   (setq yl (length (decode-coding-string ystr 'euc-japan)))
		 (setq yl (length (decode-coding-string ystr 'sjis)))))
	      (setq stdk (concat (sj3-get-stdy)
				 (sj3-get-convert-string)
				 "\C-@"))
		(set-buffer sj3-result-buffer)
		(setq p0 (point))
		(forward-char yl)
		(setq sj3-yomi-llist (append sj3-yomi-llist (list yl)))
		(insert 0)  ;;; yomi
		(sj3-put-4byte 1) ;;; kouho suu
		(sj3-put-4byte 0) ;;; current kouho number
		(insert stdk)     ;;; study data and kanji strings
		(setq offset (- (point) p0))
		(goto-char p0) (sj3-put-4byte offset)
		(goto-char (+ (point) offset))
		(setq sj3-return-code (1+ sj3-return-code))
		(set-buffer sj3-server-buffer))
	    (setq sj3-bunsetu-suu sj3-return-code)))))))
;;;
;;; entry function
;;;
(defun sj3-server-henkan-quit () t)

(defun sj3-get-yomi-suu-org ()
  (if (setq sj3-yomi-llist (cdr sj3-yomi-llist))
      (car sj3-yomi-llist)
    0))

;;;
;;; entry function
;;;
(defun sj3-server-henkan-end (bunsetu-no)
  (if (not (sj3-server-active-p)) (sj3-connection-error)
    (let ((inhibit-quit t))
      (save-excursion
	(let (length ystr len kouho-no kouho-suu p0 (ylist nil))
	  (set-buffer sj3-result-buffer)
	  (goto-char (point-min))
	  (let ((max (if (and (integerp bunsetu-no)
			      (<= 0 bunsetu-no)
			      (<= bunsetu-no sj3-bunsetu-suu))
			 bunsetu-no
		       sj3-bunsetu-suu))
		(i 0))
	    (while (< i max)
	      (setq length (sj3-get-4byte))
	      (setq p0 (point))
	      (setq ystr (sj3-get-string))
	      (setq len (1- (- (point) p0)))
	      (setq kouho-suu (sj3-get-4byte)) ;;; kouho suu
	      (setq kouho-no (sj3-get-4byte))
	      (if (and (> kouho-no 0)
		       (< kouho-no (- kouho-suu 2))
		       (> kouho-suu 3))
		  (sj3-server-b-study kouho-no))
	      (setq ylist (cons (list len ystr kouho-suu (point)) ylist))
	      (goto-char (+ p0 length))
	      (setq i (1+ i)))
	    (setq ylist (nreverse ylist))
	    (setq i 1)
	    (let ((yp 0) (op 0) (ydata (car ylist)) (ol (car sj3-yomi-llist)))
	      (while (< i max)
		(let ((yl (nth 0 ydata)))
		  (setq ylist (cdr ylist))
		  (if (and (= yp op) (= yl ol))
		      (let ((pp (+ yp yl)))
			(setq yp pp)
			(setq op pp)
			(setq ydata (car ylist))
			(setq ol (sj3-get-yomi-suu-org)))
		    (let ((str (nth 1 ydata))
			  (ent (nth 2 ydata)))
		      (setq ydata (car ylist))
		      (setq yp (+ yp yl))
		      (while (< op yp)
			(setq op (+ op ol))
			(setq ol (sj3-get-yomi-suu-org)))
		      (if (or (= ent 2) (= (nth 2 ydata) 2)) nil
			(let ((sub (- op yp)) (yl1 (nth 0 ydata)))
			  (set-buffer sj3-result-buffer)
			  (goto-char (nth 3 ydata))
			  (sj3-server-cl-study str (nth 1 ydata))
			  (if (and (not (= sub yl1)) (not (= sub (- yl1 ol))))
			      nil
			    (setq i (1+ i))
			    (setq ylist (cdr ylist))
			    (setq ydata (car ylist))
			    (if (= sub yl1) nil
			      (setq op (+ op ol))
			      (setq ol (sj3-get-yomi-suu-org))))))))
		      (setq i (1+ i))))
	    (if (or (null ydata) (= (nth 0 ydata) ol) (= (nth 2 ydata) 2))
		sj3-return-code
	      (goto-char (nth 3 ydata))
	      (sj3-server-cl-study (nth 1 ydata) "")))))))))

(defun sj3-server-cl-study (str1 str2)
  (if (not (sj3-server-active-p)) (sj3-connection-error)
    (save-excursion
      (if (= *sj3-current-server-version* 2)
	  (sj3-command-start SJ3_END_EUC)
	(sj3-command-start SJ3_END))
      (sj3-put-string* str1)
      (sj3-put-string* str2)
      (if (string= str2 "") (sj3-put-stdy-dmy)
	(let (p0)
	  (set-buffer sj3-result-buffer)
	  (setq p0 (point))
	  (set-buffer sj3-command-buffer)
	  (insert-buffer-substring sj3-result-buffer p0 (+ p0 sj3-stdy-size))))
      (sj3-command-end)
      (sj3-get-result)
      (sj3-get-return-code))))
    
(defun sj3-server-b-study (no)
  (if (not (sj3-server-active-p)) (sj3-connection-error)
    (save-excursion
      (let ((i 0) p0)
	(set-buffer sj3-result-buffer)
	(while (< i no)
	  (sj3-skip-stdy)
	  (sj3-skip-yomi)
	  (setq i (1+ i)))
	(setq p0 (point))
	(sj3-command-start SJ3_STDY)
	(insert-buffer-substring sj3-result-buffer p0 (+ p0 sj3-stdy-size))
	(sj3-command-end)
	(sj3-get-result)
	(sj3-get-return-code)))))

(defun sj3-result-goto-bunsetu (bunsetu-no)
  (goto-char (point-min))
  (let (length (i 0))
    (while (< i bunsetu-no)
      (setq length (sj3-get-4byte))
      (goto-char (+ (point) length))
      (setq i (1+ i)))))
	      
;;;
;;; entry function
;;;
(defun sj3-server-henkan-kakutei (bunsetu-no jikouho-no)
  (cond((not (sj3-server-active-p)) (sj3-connection-error))
       ((or (< bunsetu-no 0) (<= sj3-bunsetu-suu bunsetu-no))
	nil)
       (t 
	(let ((inhibit-quit t))
	  (save-excursion
	    (set-buffer sj3-result-buffer)
	    (let (kouho-suu)
	      (sj3-result-goto-bunsetu bunsetu-no)
	      (sj3-skip-length)
	      (sj3-skip-yomi)
	      (setq kouho-suu (sj3-get-4byte))
	      (if (or (< jikouho-no 0) (<= kouho-suu jikouho-no)) nil
		(delete-char 4) (sj3-put-4byte jikouho-no)
		t)))))))

;;;
;;; entry function
;;;
(defun sj3-server-henkan-next (bunsetu-no)
  (if (not (sj3-server-active-p)) (sj3-connection-error)
    (let ((inhibit-quit t))
      (save-excursion
	(let (p0 p1 kouho-suu length ystr)
	  (set-buffer sj3-result-buffer)
	  (sj3-result-goto-bunsetu bunsetu-no)
	  (sj3-skip-length)
	  (setq p0 (point))
	  (setq ystr (sj3-get-string))
	  (setq p1 (point))
	  (setq kouho-suu (sj3-get-4byte))
	  (if (> kouho-suu 1) t
	    (let ((ksuu (sj3-server-henkan-kouho ystr)) (startp 0))
	      (if (< ksuu 0) sj3-return-code
		(let (kanji stdk i)
		  (set-buffer sj3-result-buffer)
		  (sj3-skip-4byte)
		  (sj3-skip-stdy)
		  (setq kanji (sj3-get-string))
		  (if (> ksuu 1)
		      (progn
			(setq i 1)
			(set-buffer sj3-server-buffer)
			(sj3-get-4byte)
			(setq startp (point)
			      stdk (sj3-get-stdy))
			(let ((kkanji (sj3-get-string*)))
			  (if (equal kanji kkanji)
			      (setq startp (point)
				    stdk "")
			    (setq stdk (concat stdk kkanji "\0"))
			    (setq ksuu (1+ ksuu))
			    (setq i (1+ i))))
			(while (< i ksuu)
			  (sj3-get-4byte)
			  (delete-char -4)
			  (setq stdk (concat stdk
					     (sj3-get-stdy)
					     (sj3-get-convert-string)
					     "\C-@"))
			  (setq i (1+ i)))
			))
		  (set-buffer sj3-result-buffer)
		  (if (> startp 0)
		      (insert stdk))
		  (sj3-put-kata ystr)
		  (insert ystr 0)
		  (setq length (- (point) p0))
		  (goto-char p1)
		  (delete-char 4)
		  (if (<= ksuu 0)(sj3-put-4byte 3) ;;;
		    (sj3-put-4byte (+ ksuu 2)))    ;;; put kouho-suu 
		  (goto-char p0)
		  (delete-char -4)
		  (sj3-put-4byte length))
		t))))))))

(defun sj3-server-henkan-kouho (str)
  (if (not (sj3-server-active-p)) -1
    (let ((mb-str (if (= *sj3-current-server-version* 2)
		      (encode-coding-string str 'euc-japan)
		    (encode-coding-string str 'sjis)))
	  len kouho-suu)
      (setq len (length mb-str))
      (setq kouho-suu (sj3-server-henkan-kouho-suu len mb-str))
      (if (<= kouho-suu 0) nil
	(if (= *sj3-current-server-version* 2)
	    (sj3-command-start SJ3_KOUHO_EUC)
	  (sj3-command-start SJ3_KOUHO))
	(sj3-put-4byte len)
	(sj3-put-string mb-str)
	(sj3-command-end)
	(sj3-get-result)
	(sj3-get-return-code)
	(if (not (= sj3-return-code 0)) -1))
      kouho-suu)))

(defun sj3-put-kata (str)
  (sj3-save-buffer-multibyte-and-enable
   (setq str (string-as-multibyte (copy-sequence str)))
  (let ((i 0) (len (length str)) ch)
    (while (< i len)
       (setq ch (aref str i))
       (aset str i
	     (if (and (/= ?ー ch)
		      (string-match "\\cH" (char-to-string ch)))
		 (make-char 'japanese-jisx0208 37 (% ch 256))
	       ch))
       (setq i (1+ i)))
     (insert str 0))))

(defun sj3-server-henkan-kouho-suu (yomi-length yomi)
  (if (not (sj3-server-active-p)) -1
    (save-excursion
      (if (= *sj3-current-server-version* 2)
	  (sj3-command-start SJ3_KOUHOSU_EUC)
	(sj3-command-start SJ3_KOUHOSU))
      (sj3-put-4byte yomi-length)
      (sj3-put-string yomi)
      (sj3-command-end)
      (sj3-get-result)
      (sj3-get-return-code)
      (if (not (= sj3-return-code 0)) -1
	(sj3-get-4byte)))))

;;;
;;; entry function
;;;
(defun sj3-server-bunsetu-henkou (bunsetu-no bunsetu-length)
  (cond((not (sj3-server-active-p)) (sj3-connection-error))
       ((or (< bunsetu-no 0) (<= sj3-bunsetu-suu bunsetu-no))
	nil)
       (t
	(let ((inhibit-quit t))
	  (save-excursion
	    (let (yp0 p0 p1 str len1 len2 bunsetu-suu (bno bunsetu-no))
	      (set-buffer sj3-result-buffer)
	      (setq yp0 (sj3-yomi-point bunsetu-no))
	      (setq p0 (point))
	      (setq str (sj3-get-yomi yp0 bunsetu-length))
	      (setq len1 (length str))
	      (setq bunsetu-suu sj3-bunsetu-suu)
	      (let (point length)
		(setq len2 len1)
		(while (and (< bno sj3-bunsetu-suu) (> len2 0))
		  (setq length (sj3-get-4byte))
		  (setq point (point))
		  (skip-chars-forward "^\0")
		  (setq len2 (- len2 (- (point) point)))
		  (goto-char (+ point length))
		  (setq bno (1+ bno))))
	      (setq p1 (point))
	      (delete-region p0 p1)
	      (setq sj3-bunsetu-suu (- sj3-bunsetu-suu (- bno bunsetu-no)))
	      (if (= (sj3-put-tanconv str) 0)
		  (if (not (= len2 0))
		      (let ((len (- 0 len2)) (yp1 (+ yp0 len1))
			    ystr mb-str)
			(if (or (> bno (1+ bunsetu-no)) (= bno bunsetu-suu))
			    (setq ystr (sj3-get-yomi yp1 len))
			  (let (ll length i)
			    (set-buffer sj3-result-buffer)
			    (setq p0 (point))
			    (setq length (sj3-get-4byte))
			    (skip-chars-forward "^\0")
			    (setq ll (+ len (- (point) (+ p0 4))))
			    (setq p1 (+ p0 (+ length 4)))
			    (setq ystr (sj3-get-yomi yp1 ll))
			    (setq mb-str (if (= *sj3-current-server-version* 2)
					     (encode-coding-string ystr 'euc-japan)
					   (encode-coding-string ystr 'sjis)))
			    (setq i (sj3-server-henkan-kouho-suu 
				     (length mb-str) mb-str))
			    (set-buffer sj3-result-buffer)
			    (if (= i 0) (setq ystr (sj3-get-yomi yp1 len))
			      (delete-region p0 p1)
			      (setq sj3-bunsetu-suu (1- sj3-bunsetu-suu))
			      (setq len ll))
			    (goto-char p0)))
			(sj3-put-tanconv ystr))))
	      (if (= sj3-return-code -1) nil
		sj3-bunsetu-suu)))))))

(defun sj3-put-tanconv (str)
  (let ((point (point)) len ksuu
	(mb-str (if (= *sj3-current-server-version* 2)
		    (encode-coding-string str 'euc-japan)
		  (encode-coding-string str 'sjis))))
    (setq len (length mb-str))
    (setq ksuu (sj3-server-henkan-kouho-suu len mb-str))
    (if (>= ksuu 0)
	(let (offset)
	  (set-buffer sj3-result-buffer)
	  (insert str 0)
	  (if (or (= ksuu 0)
		  (not (sj3-server-tanconv len mb-str)))
	      (put-kata-and-hira str)
	    (let (stdk)
	      (set-buffer sj3-result-buffer)
	      (sj3-put-4byte 1)
	      (sj3-put-4byte 0)
	      (set-buffer sj3-server-buffer)
	      (sj3-get-4byte)
	      (setq stdk (concat
			  (sj3-get-stdy)
			  (sj3-get-convert-string)
			  "\C-@"))
	      (set-buffer sj3-result-buffer)
	      (insert stdk)))
	  (set-buffer sj3-result-buffer)
	  (setq offset (- (point) point))
	  (goto-char point) (sj3-put-4byte offset)
	  (goto-char (+ (point) offset))
	  (setq sj3-bunsetu-suu (1+ sj3-bunsetu-suu))))
    sj3-return-code))
		    
(defun sj3-server-tanconv (len str)
  (if (not (sj3-server-active-p)) (sj3-connection-error)
    (let ((inhibit-quit t))
      (if (= *sj3-current-server-version* 2)
	  (sj3-command-start SJ3_TANCONV_EUC)
	(sj3-command-start SJ3_TANCONV))
      (sj3-put-4byte len)
      (sj3-put-string str)
      (sj3-command-end)
      (sj3-get-result)
      (sj3-get-return-code))))

(defun put-kata-and-hira (str)
  (sj3-put-4byte 2)
  (sj3-put-4byte 0)
  (sj3-put-stdy-dmy)
  (sj3-put-kata str)
  (sj3-save-buffer-multibyte-and-enable
   (insert str 0)))

(defun sj3-get-yomi (offset length)
  (substring sj3-henkan-string offset (+ offset length)))

(defun sj3-bunsetu-suu () sj3-bunsetu-suu)

(defun sj3-bunsetu-kanji (bunsetu-no &optional buffer)
  (let ((savebuffer (current-buffer)))
    (unwind-protect 
	(progn
	  (set-buffer sj3-result-buffer)
	  (if (or (< bunsetu-no 0)
		  (<= sj3-bunsetu-suu bunsetu-no))
	      nil
	    (sj3-result-goto-bunsetu bunsetu-no)
	    (sj3-skip-length)
	    (sj3-skip-yomi)

	    (let ((i 0) 
		  (rksuu (- (sj3-get-4byte) 2)) ;;; real kouho-suu
		  (max (sj3-get-4byte)))       ;;; kouho-number
	      (sj3-skip-stdy)
	      (while (< i max)
		(sj3-skip-yomi)
		(setq i (1+ i))
		(if (< i rksuu)
		    (sj3-skip-stdy))))
	    
	    (let (p1 string)
	      (setq p1 (point))
	      (skip-chars-forward "^\0")
	      (setq string (buffer-substring p1 (point)))
	      (forward-char 1)
	      (if (null buffer)
		  string
		(set-buffer buffer)
		(insert string)
		nil))))
      (set-buffer savebuffer))))

(defun sj3-bunsetu-kanji-length (bunsetu-no)
  (save-excursion
    (set-buffer sj3-result-buffer)
    (if (or (< bunsetu-no 0)
	    (<= sj3-bunsetu-suu bunsetu-no))
	nil
      (sj3-result-goto-bunsetu bunsetu-no)
      (sj3-skip-length)
      (sj3-skip-yomi)

      (let ((i 0) 
	    (rksuu (- (sj3-get-4byte) 2)) ;;; real kouho-suu
	    (max (sj3-get-4byte)))        ;;; kouho-number
	(sj3-skip-stdy)
	(while (< i max)
	  (sj3-skip-yomi)
	  (setq i (1+ i))
	  (if (< i rksuu)
	      (sj3-skip-stdy))))
	    
      (let (p1)
	(setq p1 (point))
	(skip-chars-forward "^\0")
	(save-restriction
	  (narrow-to-region p1 (point))
	  (- (point-max) (point-min)))))))

(defun sj3-bunsetu-yomi-moji-suu (bunsetu-no)
  (save-excursion
    (set-buffer sj3-result-buffer)
    (if (or (<  bunsetu-no 0)
	    (<= sj3-bunsetu-suu bunsetu-no))
	nil
      (sj3-result-goto-bunsetu bunsetu-no)
      (sj3-skip-length)
      (let ((c 0))
	(while (not (zerop (char-after)))
	  (forward-char 1)
	  (setq c (1+ c)))
	c))))

(defun sj3-yomi-point (bunsetu-no)
  (let ((i 0) (len 0) point length)
    (goto-char (point-min))
    (while (< i bunsetu-no)
      (setq length (sj3-get-4byte))
      (setq point (point))
      (skip-chars-forward "^\0")
      (setq len (+ len (- (point) point)))
      (goto-char (+ point length))
      (setq i (1+ i)))
      len))

(defun sj3-bunsetu-yomi (bunsetu-no &optional buffer)
  (let ((savebuff (current-buffer)))
    (unwind-protect 
	(progn
	  (set-buffer sj3-result-buffer)
	  (if (or (<  bunsetu-no 0)
		  (<= sj3-bunsetu-suu bunsetu-no))
	      nil
	    (sj3-result-goto-bunsetu bunsetu-no)
	    (sj3-skip-length)
	    (let (p1 p2 )
	      (setq p1 (point))
	      (skip-chars-forward "^\0")
	      (if (null buffer ) (buffer-substring p1 (point))
		(setq p2 (point))
		(set-buffer buffer)
		(insert-buffer-substring sj3-result-buffer p1 p2)
		t))))
      (set-buffer savebuff))))

(defun sj3-bunsetu-yomi-equal (bunsetu-no yomi)
  (save-excursion
    (set-buffer sj3-result-buffer)
      (if (or (<  bunsetu-no 0)
	    (<= sj3-bunsetu-suu bunsetu-no))
	nil
      (sj3-result-goto-bunsetu bunsetu-no)
      (sj3-skip-length)
      (looking-at (regexp-quote yomi))))) ;93.4.6 by T.Saneto

(defun sj3-bunsetu-kouho-suu (bunsetu-no)
  (save-excursion
    (set-buffer sj3-result-buffer)
    (if (or (<  bunsetu-no 0)
	    (<= sj3-bunsetu-suu bunsetu-no))
	nil
      (sj3-result-goto-bunsetu bunsetu-no)
      (sj3-skip-length)
      (sj3-skip-yomi)
      (sj3-get-4byte))))

(defun sj3-bunsetu-kouho-list (bunsetu-no)
  (save-excursion
    (set-buffer sj3-result-buffer)
    (if (or (<  bunsetu-no 0)
	    (<= sj3-bunsetu-suu bunsetu-no))
	nil
      (sj3-result-goto-bunsetu bunsetu-no)
      (sj3-skip-length)
      (sj3-skip-yomi)
      (let ((max (sj3-get-4byte)) (i 0) (result nil) p0)
	(sj3-skip-4byte) ;;; current kouhou number
	(sj3-skip-stdy)
	(while (< i max)
	  (setq p0 (point))
	  (skip-chars-forward "^\0")
	  (setq result
		(cons (concat (buffer-substring p0 (point)))
		      result))
	  (forward-char 1)
	  (setq i (1+ i))
	  (if (< i (- max 2))
	      (sj3-skip-stdy)))
	(nreverse result)))))

(defun sj3-bunsetu-kouho-number (bunsetu-no)
  (save-excursion
    (set-buffer sj3-result-buffer)
    (if (or (<  bunsetu-no 0)
	    (<= sj3-bunsetu-suu bunsetu-no))
	nil
      (sj3-result-goto-bunsetu bunsetu-no)
      (sj3-skip-length)
      (sj3-skip-yomi)
      (sj3-skip-4byte)
      (sj3-get-4byte)))
  )

(defun sj3-simple-command (op arg)
  (if (sj3-server-active-p)
      (let ((inhibit-quit t))
	(progn
	  (sj3-command-start op)
	  (sj3-put-4byte arg)
	  (sj3-command-end)
	  (sj3-get-result)
	  (sj3-get-return-code)))
    (sj3-connection-error)))

(defun sj3-server-open-dict (dict-file-name password)
  (if (not (sj3-server-active-p))(sj3-connection-error)
    (let ((inhibit-quit t))
      (save-excursion
        (sj3-command-start SJ3_DICADD)
	(sj3-put-string dict-file-name)
	(if (stringp password)
	    (sj3-put-string password)
	  (sj3-put-string 0))
	(sj3-command-end)
	(sj3-get-result)
	(sj3-get-return-code)
	(if (not (= sj3-return-code 0)) nil
	  (let ((dict-no (sj3-get-4byte)))
	    (if (stringp password)
		(setq sj3-user-dict-list
		      (append sj3-user-dict-list (list dict-no)))
	      (setq sj3-sys-dict-list
		    (append sj3-sys-dict-list (list dict-no))))
	    dict-no))))))

(defun sj3-server-close-dict (dict-no)
  (if (not (sj3-server-active-p))(sj3-connection-error)
    (let ((inhibit-quit t))
      (save-excursion
	(sj3-command-start SJ3_DICDEL)
	(sj3-put-4byte dict-no)
	(sj3-command-end)
	(sj3-get-result)
	(sj3-get-return-code)))))

(defun sj3-server-make-dict (dict-file-name)
  (if (not (sj3-server-active-p))(sj3-connection-error)
    (let ((inhibit-quit t))
      (save-excursion
	(sj3-command-start SJ3_MKDIC)
	(sj3-put-string dict-file-name)
	(sj3-put-4byte 2048)  ;;; Index length
	(sj3-put-4byte 2048)  ;;; Length
	(sj3-put-4byte 256)   ;;; Number
	(sj3-command-end)
	(sj3-get-result)
	(sj3-get-return-code)))))

(defun sj3-server-open-stdy (stdy-file-name)
  (if (not (sj3-server-active-p))(sj3-connection-error)
    (let ((inhibit-quit t))
      (save-excursion
	(sj3-command-start SJ3_OPENSTDY)
	(sj3-put-string stdy-file-name)
	(sj3-put-string 0)
	(sj3-command-end)
	(sj3-get-result)
	(sj3-get-return-code)))))

(defun sj3-server-close-stdy ()
  (sj3-zero-arg-command SJ3_CLOSESTDY))

(defun sj3-server-make-stdy (stdy-file-name)
  (if (not (sj3-server-active-p))(sj3-connection-error)
    (let ((inhibit-quit t))
      (save-excursion
	(sj3-command-start SJ3_MKSTDY)
	(sj3-put-string stdy-file-name)
	(sj3-put-4byte 2048)  ;;; Number
	(sj3-put-4byte 1)     ;;; Step
	(sj3-put-4byte 2048)  ;;; Length
	(sj3-command-end)
	(sj3-get-result)
	(sj3-get-return-code)))))

(defun sj3-server-dict-add (dictno kanji yomi bunpo)
  (if (not (sj3-server-active-p))(sj3-connection-error) 
    (let ((inhibit-quit t))
      (save-excursion
	(if (= *sj3-current-server-version* 2)
	    (sj3-command-start SJ3_WREG_EUC)
	  (sj3-command-start SJ3_WREG))
	(sj3-put-4byte dictno)
	(sj3-put-string* yomi)
	(sj3-put-string* kanji)
	(sj3-put-4byte bunpo)
	(sj3-command-end)
	(sj3-get-result)
	(sj3-get-return-code)))))

(defun sj3-server-dict-delete (dictno kanji yomi bunpo)
  (if (not (sj3-server-active-p)) (sj3-connection-error)
    (let ((inhibit-quit t))
      (save-excursion
	(if (= *sj3-current-server-version* 2)
	    (sj3-command-start SJ3_WDEL_EUC)
	  (sj3-command-start SJ3_WDEL))
	(sj3-put-4byte dictno)
	(sj3-put-string* yomi)
	(sj3-put-string* kanji)
	(sj3-put-4byte bunpo)
	(sj3-command-end)
	(sj3-get-result)
	(sj3-get-return-code)))))

(defun sj3-server-dict-info (dict-no)
  (if (not (sj3-server-active-p)) (sj3-connection-error)
    (let ((inhibit-quit t))
      (save-excursion
	(let ((result nil))
	  (set-buffer sj3-server-buffer)
	  (if (= *sj3-current-server-version* 2)
	      (sj3-simple-command SJ3_WSCH_EUC dict-no)
	    (sj3-simple-command SJ3_WSCH dict-no))
	  (while (= sj3-return-code 0)
	    ;;; (sj3-get-4byte)
	    (setq result (cons (list (sj3-get-string*)
				    (sj3-get-string*) (sj3-get-4byte)) result))
	    (if (= *sj3-current-server-version* 2)
		(sj3-simple-command SJ3_WNSCH_EUC dict-no)
	      (sj3-simple-command SJ3_WNSCH dict-no)))
	  (if (= sj3-return-code 111)
	      (setq sj3-error-code nil))
	  (nreverse result))))))

(defun sj3-server-make-directory (dir-name)
  (if (not (sj3-server-active-p)) (sj3-connection-error)
    (let ((inhibit-quit t))
      (save-excursion
	(sj3-command-start SJ3_MKDIR)
	(sj3-put-string dir-name)
	(sj3-command-end)
	(sj3-get-result)
	(sj3-get-return-code)))))

(defun sj3-server-file-access (file-name access-mode)
  (if (not (sj3-server-active-p)) (sj3-connection-error)
    (let ((inhibit-quit t))
      (save-excursion
	(sj3-command-start SJ3_ACCESS)
	(sj3-put-string file-name)
	(sj3-put-4byte access-mode)
	(sj3-command-end)
	(sj3-get-result)
	(setq sj3-error-code nil)
	(sj3-get-4byte)))))

(defun sj3_lock ()
  (sj3-zero-arg-command SJ3_LOCK))

(defun sj3_unlock ()
  (sj3-zero-arg-command SJ3_UNLOCK))

(defun sj3_version ()
  (sj3-zero-arg-command SJ3_VERSION))

(defconst *sj3-error-alist*
  '(
    (1 :SJ3_SERVER_DEAD
       "サーバが死んでいます。")
    (2 :SJ3_SOCK_OPEN_FAIL
       "socketのopenに失敗しました。")
    (6 :SJ3_ALLOC_FAIL
       "メモリallocで失敗しました。")
    (7 :SJ3_ILLEGAL_COMMAND
       "コマンドが間違っている")
    (12 :SJ3_BAD_HOST
	" ホスト名がない ")
    (13 :SJ3_BAD_USER
	" ユーザ名がない ")
    (31 :SJ3_NOT_A_DICT
	"正しい辞書ではありません。")
    (35 :SJ3_NO_EXIST     
	"ファイルが存在しません。")
    (37 :SJ3_OPENF_ERR
	"ファイルがオープンできません。")
    (39 :SJ3_PARAMR
	"ファイルの読み込み権限がありません。")
    (40 :SJ3_PARAMW
	"ファイルの書き込み権限がありません。")
    (71 :SJ3_NOT_A_USERDICT
	"指定されて辞書は、ユーザー辞書ではありません。")
    (72 :SJ3_RDONLY
	"リードオンリーの辞書に登録しようとしました。")
    (74 :SJ3_BAD_YOMI
	"読みに不適当な文字が含まれています。")
    (75 :SJ3_BAD_KANJI
	"漢字に不適当な文字が含まれています。")
    (76 :SJ3_BAD_HINSHI
	"指定された品詞番号がありません。")
    (82 :SJ3_WORD_ALREADY_EXIST
	"指定された単語はすでに存在しています。")
    (84 :SJ3_JISHOTABLE_FULL
	"辞書テーブルが一杯です。")
    (92 :SJ3_WORD_NO_EXIST
	"指定された単語が存在しません。")
    (102 :SJ3_MKDIR_FAIL
	" ディレクトリを作り損なった ")
    ))

(defun sj3-error-symbol (code)
  (let ((pair (assoc code *sj3-error-alist*)))
    (if (null pair)
	(list ':sj3-unknown-error-code code)
      (car (cdr pair)))))
