view redit-client-sm.el @ 40:1978dc75b15c

*** empty log message ***
author gongo
date Tue, 19 Aug 2008 16:36:10 +0900
parents dd22e85d6eab
children 991526bc3f16
line wrap: on
line source

;; 	$Id$

;; cl-macs でマクロとして定義されているため
;; cl.el をインクルードしておく
(require 'cl)

;;
;; REP Command
;;
;;  32 bits    32       32       32      32       32      textsiz
;; o-------o--------o--------o-------o--------o---------o-------...
;; +-------+--------+--------+-------+--------+---------+------+
;; | cmd   | session| editor | seqid | lineno | textsiz | text |
;; |       | id     | id     |       |        |         |      |
;; +-------+--------+--------+-------+--------+---------+------+
;; o-------header section (network order)---------------o-------...
;;
;; textsiz:
;;    This size is number of bytes for character string encoded with UTF8.
;;    
;; text:
;;    This size is variable length (textsiz).
;;

(defvar redit-client-process nil
  "Remote-edit client process.")

(defvar redit-client-buffer nil
  "Remote-edit client's buffer.")

(defvar redit-client-buffer-name nil
  "Remote-edit client's buffer-name.")

(defvar redit-client-buffer-number nil
  "Remote-edit client's buffer-number.")

(defvar redit-client-editor-id "0"
  "Remote-edit client's editor-id.")

(defvar redit-client-session-id 0
  "Remote-edit client's session-id.")

;; editor local sequence number
(defvar redit-client-seqno 0
  "Remote-edit client's editor-id.")

(defvar redit-client-line-max-in-server nil
  "Value of point-max in server's buffer.")

(defvar redit-client-previous-edited-line 1
  "Current cursor line of remote-edit client.")

(defvar redit-client-after-edited-line 1
  "Current cursor line of remote-edit client.")

(defvar redit-client-previous-line 1
  "Behind cursor line of remote-edit client.")

(defvar redit-max-string-length 1004
  "Read-Write max length of string.")

;; REP が使用する文字コード
(defconst rep-string-encoding 'utf-8)

;; port
(defconst redit-process-port 8766)

(defconst redit-open-command           1)
(defconst redit-open-ack               2)
(defconst redit-read-command           3)
(defconst redit-read-ack               4)
(defconst redit-read-finish-ack        5) ;; obsolete
(defconst redit-insert-command         6)
(defconst redit-insert-ack             7)
;;(defconst redit-write-still-ack        "7")
;;(defconst redit-write-finish-ack       "8")
(defconst redit-delete-line-command    9)
(defconst redit-delete-line-ack       10)
(defconst redit-close-command         11)
(defconst redit-close-ack             12)
(defconst redit-replace-command       13)
(defconst redit-replace-ack           14)
(defconst redit-nop-command           15)

;; REP Session
(defconst redit-join-command           41)
(defconst redit-join-ack-command       42)
(defconst redit-get-command            43) ;; obsolete
(defconst redit-get-ack-command        44) ;; obsolete
(defconst redit-put-command            45)
(defconst redit-put-ack-command        46)
(defconst redit-select-command         47) ;; obsolete
(defconst redit-select-ack-command     48)
(defconst redit-register-command       49) ;; obsolete
(defconst redit-register-ack-command   50) ;; obsolete
(defconst redit-deregister-command     51) ;; obsolete
(defconst redit-deregister-ack-command 52) ;; obsolete
(defconst redit-quit-command           53)
(defconst redit-quit-ack-command       54)

;;;;;;;;;;;;;;;;;;
;; Queue struct ;;
;;;;;;;;;;;;;;;;;;
(defstruct Queue (front nil) (rear nil))

;; memo
;;  car: return first element of list
;;  cdr: return elements list since the second of list
;;  (ex.)
;;     (car '(rose violet daisy buttercup)) => rose
;;     (cdr '(rose violet daisy buttercup)) => (violet daisy buttercup)

;; enqueue item in queue
(defun enqueue (queue item)
  (let ((new-cell (list item)))
    (if (Queue-front queue)
  	;; update last cell
  	(setf (cdr (Queue-rear queue)) new-cell)
      ;; if queue is empty
      (setf (Queue-front queue) new-cell))
    (setf (Queue-rear queue) new-cell)))

;; dequeue
(defun dequeue (queue)
  (if (Queue-front queue)
      (prog1
  	  (pop (Queue-front queue))
  	(unless (Queue-front queue)
  	  ;; if queue is empty
  	  (setf (Queue-rear queue) nil)))))

(defun dequeue-all (queue)
  "clean queue"
  (while (Queue-front queue)
    (dequeue queue)))

(defun redit-get-command-from-queue (queue)
  (nth 0 (Queue-front queue)))
(defun redit-get-line-from-queue (queue)
  (nth 1 (Queue-front queue)))
(defun redit-get-uid-from-queue (queue)
  (nth 2 (Queue-front queue)))
(defun redit-get-text-from-queue (queue)
  (nth 3 (Queue-front queue)))

;;;;;;;;;;;;;;;;;;;;;;;;;
;; Session Info struct ;;
;;;;;;;;;;;;;;;;;;;;;;;;;

;; eid: Editor ID corresponding to Session ID
;; send_cmdlist:
;;      rep_command list from user
;; recv_cmdlist:
;;      rep_command list from Session Manager
(defstruct SessionInfo (eid nil) (send_cmdlist nil) (recv_cmdlist nil))

;; create and initialize
(defun redit-create-session-info (eid)
  (make-SessionInfo :eid eid
		    :send_cmdlist (make-Queue)
		    :recv_cmdlist (make-Queue)))

(defun redit-get-editor-id-from-session-info (sinfo)
  (SessionInfo-eid sinfo))
(defun redit-get-sendqueue-from-session-info (sinfo)
  (SessionInfo-send_cmdlist sinfo))
(defun redit-get-recvqueue-from-session-info (sinfo)
  (SessionInfo-recv_cmdlist sinfo))

;;;;;;;;;;;;;;;;;
;; hash table  ;;
;;;;;;;;;;;;;;;;;

(defvar bufname-to-sid-table (make-hash-table :test #'equal))
(defvar sid-to-session-table (make-hash-table))

(defun redit-set-sid-to-bufname-table (bufname sid)
  (setf (gethash bufname bufname-to-sid-table) sid))
(defun redit-get-sid-from-bufname-table (bufname)
  (gethash bufname bufname-to-sid-table))

(defun redit-set-session-info-to-sid-table (sid sinfo)
  (setf (gethash sid sid-to-session-table) sinfo))
(defun redit-get-session-info-from-sid-table (sid)
  (gethash sid sid-to-session-table))

(defun gongo-start ()
  (interactive)
  (add-hook 'before-change-functions
	    'redit-client-before-change-function t t)
  (add-hook 'after-change-functions
	    'redit-client-after-change-function t t)
)

(defun gongo-stop ()
  (interactive)
  (remove-hook 'before-change-functions
	    'redit-client-before-change-function t)
  (remove-hook 'after-change-functions
	    'redit-client-after-change-function t)
)


;;;;;;;;;;;;;;;;;;;
;; pack / unpack ;;
;;;;;;;;;;;;;;;;;;;
(defun redit-pack-int-loop (num count)
  (if (> count 0)
      (concat
       (redit-pack-int-loop (/ num 256) (- count 1))
       (char-to-string (% num 256)))))

(defun redit-pack-int (num)
  (redit-pack-int-loop num 4))

(defun redit-pack (cmd sid eid seq lno siz)
  (concat
   (redit-pack-int cmd) (redit-pack-int sid) (redit-pack-int eid)
   (redit-pack-int seq) (redit-pack-int lno) (redit-pack-int siz)))

(defun redit-unpack-int-loop (pkt pos count)
  (if (> count 1)
      (+
       (* (redit-unpack-int-loop pkt (- pos 1) (- count 1)) 256)
       (string-to-char (substring pkt (- pos 1) pos)))
    0
    ))

(defun redit-unpack-int (pkt pos)
  (redit-unpack-int-loop pkt (* pos 4) 4))


;;;;;;;;;;;;;;;;;;
;; User Command ;;
;;;;;;;;;;;;;;;;;;

(defun redit-join-command ()
  "Allow this Emacs process to be a remote-edit session manager for client processes."
  (interactive)
  (let (bufname)
    (setq bufname (encode-coding-string
		   (buffer-name)
		   rep-string-encoding))

    (if redit-client-process
	(progn
	  (set-process-sentinel redit-client-process nil)
	  (condition-case () ;;
	      (delete-process redit-client-process) (error nil))))
    
    (setq redit-client-process (open-network-stream
				"redit-client" nil "localhost"
				redit-process-port))

    ;; called when the process changes state
    (set-process-sentinel redit-client-process 'redit-client-sentinel)
    
    ;; called when the process receive packet
    (set-process-filter redit-client-process 'redit-client-process-filter)
    
    ;; //Code of the process input/output buffer is rep-string-encoding
    ;; //(set-process-coding-system redit-client-process rep-string-encoding rep-string-encoding)
    ;; 送受信するデータはバイナリで貰わないといけない。
    ;; utf-8 じゃなくてずれる可能性があるので
    (set-process-coding-system redit-client-process 'binary 'binary)
    
    ;; プロセスが生きてるとき、 emacs を終了しようとすると
    ;; 「processがうんたらで、本当に切ってもいいの y/n」
    ;; って聞かれる。nil てやっておくと聞かれない。
    ;; quit を入れるんなら t にして有効にすべきなんだろうか。
    ;; 今は nil で
    (set-process-query-on-exit-flag redit-client-process nil)
    
    ;; not necessary since Emacs 21.1
    ;; (make-local-hook 'before-change-functions)
    ;; (make-local-hook 'after-change-functions)
    
    (if (string= "*scratch*" bufname) 
	(progn
	;; JOIN if buffer-name is "*scratch*"
	  (process-send-string
	   redit-client-process
	   (concat
	    (redit-pack
	     redit-join-command
	     0 0 0 0 (string-bytes bufname))
	    (buffer-name)))
	  (message (concat
		    "join-command: "
		    (number-to-string redit-join-command) " "
		    (number-to-string 0) " "
		    (number-to-string 0) " "
		    (number-to-string 0) " "
		    (number-to-string 0) " "
		    (number-to-string (string-bytes bufname)) " " 
		    "\"" bufname "\""
		    )))
      ;; PUT if buffer-name is not "*scratch*"
      (progn
	(process-send-string
	 redit-client-process
	 (concat
	  (redit-pack
	   redit-put-command
	   0 0 0 0 (string-bytes bufname))
	  (buffer-name)))
	(message "put-command"))
      )))

(defun redit-quit-command ()
  "quit"
  (interactive)
  ;; process kill とかしよう
  )


;; obsolete
(defun redit-client-open (file)
  (interactive "P")
  (if redit-client-process
      (if (setq file (read-string "Filename: "))
	  (progn
	    ;; redit-client-process の input に文字列を送信する
	    (process-send-string
	     redit-client-process
	     ;; redit-open-command (01) と file を連結させる
	     (concat redit-open-command file))
	    ;; redit-client-process から出力を得るまでループ
	    (while (eq nil (accept-process-output redit-client-process)))))
    (error "redit-client is not running.")))

;; redit-client-process にreadコマンドとバッファ番号、行番号を送り、
;; サーバからの出力を得る
(defun redit-client-read-line (linenum)
  (if redit-client-process
      (save-excursion
	(progn
	  (process-send-string
	   redit-client-process
	   (format "%10d%10d%10d%10d%10d%10d\n" redit-read-command redit-client-session-id redit-client-editor-id (gen-seqno) linenum 0))
	  (while (eq nil
		     (accept-process-output redit-client-process)))))
    (error "redit-client is not running.")))


;; linenumで指定された行の編集をサーバへ送る
;; redit-client-process に insert コマンドと
;; バッファ番号、行番号、行の内容を送り、 Ack を受け取る
(defun redit-client-insert-line (linenum isnewline)
  (if redit-client-process
      (save-excursion
	(let ((beginp) (endp) (text) (length) (packet) (sinfo))
	     (setq beginp
		   (progn (goto-line linenum) (beginning-of-line) (point)))
	     (setq endp
		   (progn (goto-line linenum) (end-of-line) (point)))
	     (setq text
		   (encode-coding-string
		    (buffer-substring beginp endp) rep-string-encoding))
	     (setq length (string-bytes text))
	     
	     ;;(setq packet (concat (redit-pack redit-insert-command
		;;			      redit-client-session-id
			;;		      redit-client-editor-id
				;;	      (gen-seqno) linenum length)
	     (setq redit-client-session-id
		   (redit-get-sid-from-bufname-table "test.c"))
	     (setq sinfo
		   (redit-get-session-info-from-sid-table
		    redit-client-session-id))
	     (setq redit-client-editor-id
		   (redit-get-editor-id-from-session-info sinfo))

	     (message (concat
		       "cmd = " (number-to-string redit-insert-command) ", "
		       "sid = " (number-to-string redit-client-session-id) ", "
		       "eid = " (number-to-string redit-client-editor-id) ", "
		       "seq = " "0" ", "
		       "lno = " (number-to-string linenum) ", "
		       "siz = " (number-to-string length) "\n"
		       "text= " text))
	     (setq packet
		   (concat
		    (redit-pack
		     redit-insert-command
		     redit-client-session-id
		     redit-client-editor-id
		     (gen-seqno) linenum length)
		    text
		    (if (eq isnewline t) "\n")))	     
	     (process-send-string redit-client-process packet)))
    (error "redit-client is not running.")))

;; linenum で指定した行の削除命令を redit-client-process に送信する
(defun redit-client-delete-line (linenum)
  (if redit-client-process
      (save-restriction

	;; トークンが回ってくるのを待つ場合は、
	;; すぐに process-send-string せずに
	;; 編集情報を保存するだけにとどめる
	;;(enqueue redit-client-command-queue
	;;(concat (format "%10d%10d%10d%10d%10d%10d" (string-to-number redit-delete-line-command) redit-client-session-id redit-client-editor-id (gen-seqno) linenum 0) "\n"))
	
	;; insert lines on server buffer
	(process-send-string
	 redit-client-process
	 (concat
	  (redit-pack redit-delete-line-command
		      redit-client-session-id
		      redit-client-editor-id
		      (gen-seqno) linenum 0))))
    (error "redit-client is not running.")))

;; redit-client-process へcloseコマンドを送る
(defun redit-client-close ()
  "Save File and exit Remote-edit client."
  (interactive)
  (let ((line redit-client-previous-line))
    (redit-client-insert-line line nil)
    (process-send-string
     redit-client-process
     (concat
      ;; obsolete send string
      ;; (format "%10d%10d%10d%10d%10d%10d" (string-to-number redit-close-command) redit-client-session-id redit-client-editor-id (gen-seqno) 0 0)
      (redit-pack
       redit-close-command
       redit-client-session-id
       redit-client-editor-id
       (gen-seqno) 0 0) "\n"))))

;; redit-client-process を終了させる
(defun redit-client-kill ()
  "Stop Remote-Edit client process."
  (interactive)
  (if redit-client-process
      (kill-process redit-client-process))
  (if redit-client-buffer
      (kill-buffer redit-client-buffer))
  (kill-all-local-variables))

;; proc == redit-client-process
;; string == redit-client-process からの output
(defun redit-client-process-filter (proc string)
  ;; バッファローカルなフックリスト before-change-functions から
  ;; redit-client-before-change-function を取り除く. after も同じ
  ;; proc からの出力に hook しないように
  (remove-hook 'before-change-functions
	       'redit-client-before-change-function t)
  (remove-hook 'after-change-functions
	       'redit-client-after-change-function t)

  (message (concat "redit-client-process-filter: " string))
  (let ((command (rep-get-command-from-pkt string)))
    ;; command がどの命令かを判断し、対応した処理をする。case みたい
    (cond 
     ;; FIXME
     ;;((if (= command redit-open-ack)
     ;;(if (/= redit-client-editor-id (rep-get-editor-id-from-pkt string))
     ;;(redit-client-exec-open-ack string))))

     ((if (= command redit-read-ack)
	  (if (= redit-client-editor-id (rep-get-editor-id-from-pkt string))
	      (redit-client-exec-read-ack string))))

     ((if (= command redit-insert-ack)
	  (if (= redit-client-editor-id (rep-get-editor-id-from-pkt string))
	      (redit-client-exec-insert-ack string))))

     ((if (= command redit-delete-line-ack)
	  (if (= redit-client-editor-id (rep-get-editor-id-from-pkt string))
	      (redit-client-exec-delete-line-ack string))))

     ((if (= command redit-close-ack)
	  (if (= redit-client-editor-id (rep-get-editor-id-from-pkt string))
	      (redit-client-exec-close-ack string))))

     ((if (= command redit-insert-command)
	  (if (/= redit-client-editor-id (rep-get-editor-id-from-pkt string))
	      (redit-client-exec-insert-line string))))

     ;; from Session Manager
     ;; join ack (editor id)
     ((if (= command redit-join-ack-command)
	  (redit-client-exec-join string)))

     ;; put ack (editor id)
     ((if (= command redit-put-ack-command)
	  (redit-client-exec-put string)))

     ;; delete line
     ((if (= command redit-delete-line-command)
	  (if (/= redit-client-editor-id (rep-get-editor-id-from-pkt string))
	      (redit-client-exec-delete-line string))))
     ))

  (add-hook 'before-change-functions
	    'redit-client-before-change-function t t)
  (add-hook 'after-change-functions
	    'redit-client-after-change-function t t)
  )

;; window-scroll-functions に hook される。
;; window がスクロールする度に呼ばれる
(defun redit-client-update-function (window window-start)
  (if (equal (window-buffer) redit-client-buffer)
      (save-excursion
	(remove-hook 'before-change-functions
		     'redit-client-before-change-function t)
	(remove-hook 'after-change-functions
		     'redit-client-after-change-function t)
	;; read part.
	(while (and (= (point-max)
		       (window-end nil t))
		    (> redit-client-line-max-in-server
		       (real-count-lines (window-end nil t))))
	  ;; 		       (count-lines (window-end nil t))))
	  (redit-client-read-line (real-count-lines (window-end nil t))))
	
	(if (not (= (count-lines (point-min) (window-end nil t))
		    (real-count-lines (window-end nil t))))
	    (redit-client-read-line
	     (progn (goto-line redit-client-line-max-in-server)
		    (real-count-lines (point)))))
	
	(if (< redit-client-line-max-in-server
	       (real-count-lines (window-end nil t)))
	    (progn (goto-line (window-end nil t))
		   (delete-region
		    (progn (beginning-of-line) (backward-char) (point))
		    (progn (forward-char) (end-of-line) (point)))))
	
	(add-hook 'before-change-functions
		  'redit-client-before-change-function t t)
	(add-hook 'after-change-functions
		  'redit-client-after-change-function t t))))

;; currentp の位置の行数を返す
(defun real-count-lines (currentp)
  (+ (count-lines (point-min) currentp)
     (if (= (save-excursion (goto-char currentp) (current-column))
	    0)
	 1  0)))

(defun limited-save-excursion (&rest exprs)
  (let ((saved-point (point)))
    (while exprs
      (eval (car exprs))
      (setq exprs (cdr exprs)))
    (goto-char saved-point)))

;; before-change-functions に hook される
;; バッファに変更があるたびに呼ばれる。バッファが変更される前に呼ばれる。
;; begin と end には変更前の変更部分の始まりと終わりの point が入る
(defun redit-client-before-change-function (begin end)
  (let ((beginl (real-count-lines begin)) ;; begin の行番号
	(endl (real-count-lines end))	  ;; end の行番号
	(currline))			  ;; currline = nil

    ;; (message "before-change-function")

    ;; 前回の編集した行と今回の編集した行が
    ;; 違っていたら前回の編集をサーバへ送る
    (if (not (= beginl redit-client-previous-edited-line))
	(redit-client-insert-line redit-client-previous-edited-line nil))

    ;; 削除処理で起こる。
    ;; (ex. BackSpaceによる行そのものの削除, C-Space C-wなどのカット, ...)
    (if (< beginl endl)
	(progn
	  (setq currline endl)
	  ;; endlからbeginlまで行の削除命令を出す
	  (while (> currline beginl) 
	    (redit-client-delete-line endl)
	    (setq currline (- currline 1)))))))

;; after-change-functions に hook される
;; バッファに変更があるたびに呼ばれる。バッファが変更された後に呼ばれる
;; begin と end には変更後の変更部分の始まりと終わりの point が入る
(defun redit-client-after-change-function (begin end length)
  (let ((beginl (real-count-lines begin))
	(endl (real-count-lines end))
	(currline))

    ;; 挿入処理で起こる。
    ;; (ex. 改行, C-y などのペースト, ...)
    (if (< beginl endl)
	(progn
	  (setq currline beginl)
	  ;; beginlからendlまで挿入命令を出す
	  (while (<= currline endl)
	    (progn (redit-client-insert-line currline nil)
		   (setq currline (+ currline 1))))))
    (setq redit-client-previous-edited-line endl)))

;; 引き数で与えられた string (line_num + text_data) から
;; 指定された行を削除し、そこに text_data を挿入する
(defun redit-client-exec-insert-line (string)
  (let ((linenum (rep-get-line-number-from-pkt string)) ;; 行番号
	(text (rep-get-text-from-pkt string))) ;; テキストデータ
    (if (< (real-count-lines (point-max)) linenum)
	(progn
	  (call-interactively 'end-of-buffer) (newline)))
    (goto-line linenum)
    (delete-region (progn (beginning-of-line) (point)) ;; 行の頭から
		   (progn (end-of-line) (point))) ;; 行の末尾まで削除
    (insert text) ;; テキストデータを挿入
    ))

;; 引き数 string (line_num + text_data) で指定された行を削除する
(defun redit-client-exec-delete-line (string)
  (let ((linenum (rep-get-line-number-from-pkt string)))
    (goto-line linenum)
    ;; 行頭から末尾までのテキストを削除
    (delete-region (progn (beginning-of-line) (point))
		   (progn (end-of-line) (point)))
    ;; 指定された行自体を削除
    ;; 指定された行番号は別の行の番号を表すことになる
    (if (= (point) (point-max))
	(delete-backward-char 1)
      (delete-char 1)))
  )

;; 何用?相手が開いてるファイルを取得するのかな?
;; read みたいなもん?
;; 引き数 string (buf_num + line_num + text_data) 
;;(defun redit-client-exec-open-ack (string)
;;  (save-excursion
;;    (let ((bufnum (rep-get-editor-id-from-pkt string))
;;	  (linenum (rep-get-line-number-from-pkt string)))
;;      (make-variable-buffer-local 'redit-client-buffer-name)
;;      (make-variable-buffer-local 'redit-client-editor-id)
;;      (setq redit-client-buffer-name
;;	    (rep-get-text-from-pkt string))
;;      (setq redit-client-buffer (get-buffer-create redit-client-buffer-name))
;;      ;; プロセスとバッファを関連づける
;;      (set-process-buffer redit-client-process redit-client-buffer)
;;      (switch-to-buffer redit-client-buffer) ;; windowのバッファを変更する
;;      (make-variable-buffer-local 'redit-client-previous-edited-line)
;;      (make-variable-buffer-local 'redit-client-after-edited-line)
;;      (make-variable-buffer-local 'redit-client-line-max-in-server)
;;      (make-variable-buffer-local 'redit-client-previous-line)
;;      (setq redit-client-line-max-in-server linenum)
;;      (setq redit-client-editor-id bufnum)
;;      (setq redit-client-previous-edited-line 1)
;;      (setq redit-client-after-edited-line 1)
;;      (setq redit-client-previous-line 1)
;;
;;      ;;(add-hook 'before-change-functions
;;      ;;'redit-client-before-change-function t t)
;;      ;;(add-hook 'after-change-functions
;;      ;;'redit-client-after-change-function t t)
;;
;;      ;; not necessary since Emacs 21.1
;;      ;; (make-local-hook 'window-scroll-functions)
;;
;;      ;; window がスクロールする度に呼ばれる関数群
;;      (add-hook 'window-scroll-functions
;;		'redit-client-update-function t t))))


;; string (line_num + text_data) で指定された行に
;; text_data を挿入する
(defun redit-client-exec-read-ack (string)
  (save-excursion
    (let ((lines (rep-get-line-number-from-pkt string)))
      ;; redit-client-buffer をカレントバッファにする
      (set-buffer redit-client-buffer)
      (goto-line lines)
      (beginning-of-line) ;; 行の始めへ移動
      ;;      (delete-region (progn (beginning-of-line) (point))
      ;; 		     (progn (end-of-line) (point)))
      (insert (rep-get-text-from-pkt string))))) ;; テキストデータを挿入

;; redit-client-line-max-in-server に行番号を入れてる(だけ)
(defun redit-client-exec-insert-ack (string)
  (save-excursion
    (set-buffer redit-client-buffer)
    (let ((linenum (rep-get-line-number-from-pkt string )))
      (setq redit-client-line-max-in-server linenum))))

(defun redit-client-exec-delete-line-ack (string)
  (save-excursion
    (set-buffer redit-client-buffer)
    (let ((linenum (rep-get-line-number-from-pkt string)))
      (setq redit-client-line-max-in-server linenum))))

;; プロセスとバッファ、ローカル変数を削除する
(defun redit-client-exec-close-ack (string)
  (if redit-client-process
      (kill-process redit-client-process))
  (if redit-client-buffer
      (kill-buffer redit-client-buffer))
  (kill-all-local-variables))

;; 引き数で与えられた string から Editor IDを取得する
(defun redit-client-exec-join (string)
  (let (sinfo)
    (setq redit-client-editor-id (rep-get-editor-id-from-pkt string))
    (setq redit-client-session-id (rep-get-session-id-from-pkt string))
  
    (setq sinfo (redit-create-session-info redit-client-editor-id))
    (redit-set-session-info-to-sid-table redit-client-session-id sinfo)
    (redit-set-sid-to-bufname-table "test.c" redit-client-session-id)
    
    (message (concat "Session ["
		     (number-to-string redit-client-session-id)
		     "] "
		     "test.c"))
    (message (concat "Your editor id = " (number-to-string redit-client-editor-id)))
))

;; //引き数で与えられた string から Session IDを取得する
;; 新仕様で、(put = ファイル有りjoin) ってことになって
;; editor id を返すことになったので、sid と共に eid もセーブする
(defun redit-client-exec-put (string)
  (let (sinfo)
    (setq redit-client-editor-id (rep-get-editor-id-from-pkt string))
    (setq redit-client-session-id (rep-get-session-id-from-pkt string))

    (setq sinfo (redit-create-session-info redit-client-editor-id))
    (redit-set-session-info-to-sid-table redit-client-session-id sinfo)
    (redit-set-sid-to-bufname-table "test.c" redit-client-session-id)
    
    (message (concat "Session ["
		     (number-to-string redit-client-session-id)
		     "] "
		     "test.c" "\n"))
    (message (concat "Your editor id = " (number-to-string redit-client-editor-id) "\n"))))
  
;; SessionIDを名前とした、新しいバッファを開く
;; FIXME
;;   SessionIDではなく、Session Nameが望ましい?
;;   バッファを開いた後、Sessionで開いてるファイルの中身を表示するべき
(defun redit-client-exec-select ()
  (switch-to-buffer (get-buffer-create
		     (number-to-string redit-client-session-id))))

;; プロセスの状態を見て、対応したメッセージを表示
(defun redit-client-sentinel (proc msg)
  (cond ((eq (process-status proc) 'exit)
	 (message "Client subprocess exited"))
	((eq (process-status proc) 'signal)
	 (message "Client subprocess killed"))))

(defun gen-seqno ()
  "generate editor local sequence number."
  (setq redit-client-seqno (+ redit-client-seqno 1)))

(defun redit-make-packet (cmd sid eid seq lno siz text)
  (concat (redit-pack cmd sid eid seq lno siz) text))

(defun rep-get-command-from-pkt (pkt)
  (redit-unpack-int pkt 1))
(defun rep-get-session-id-from-pkt (pkt)
  (redit-unpack-int pkt 2))
(defun rep-get-editor-id-from-pkt (pkt)
  (redit-unpack-int pkt 3))
(defun rep-get-sequence-id-from-pkt (pkt)
  (redit-unpack-int pkt 4))
(defun rep-get-line-number-from-pkt (pkt)
  (redit-unpack-int pkt 5))
(defun rep-get-text-size-from-pkt (pkt)
  (redit-unpack-int pkt 6))

;; コマンド後ろにあるテキストは
;;   (substring packet 24)
;; でとれるので、text-size は要らない、とうれしい。
;; が、(通信テストまだなので)例えば連続してパケットが送られて来たとき
;; そのパケットが繋がってしまうと駄目。
;; もし、process-filter が、一つのパケットの処理が終わるまで
;; 後続のパケットを止めててくれるならこれでいけるが。
;; 念のため、受け取った text-size と比較して
;; 同じならおk、だめならそのサイズ分だけ取る、って所か。
;; 最初からサイズ分だけでいいんじゃね?と思ったりもするが。
;; あと、現時点ではサイズが違う(失敗)時の処理いれてない。
(defun rep-get-text-from-pkt (pkt)
  (let ((size) (text))
    (setq size (rep-get-text-size-from-pkt pkt))
    (setq text (substring pkt 24))
    (decode-coding-string text rep-string-encoding)))
    ;;(if (= size (string-bytes text)) text
    ;;(decode-coding-string (apply 'concat (split-string text "\xe3"))
    ;;rep-string-encoding))))

(defun redit-line-translate-out (cque rque)
  "redit line translate for output"
  (let ((cc) (rc) (xcc) (xrc) (ccc) (crc) (cignore 0) (rignore 0))
    (setq xcc 0)
    (setq xrc 0)
    (setq cignore 0)
    (setq rignore 0)
    (while cque
      (setq cc (car cque))
      (while rque
	(setq rc (car rque))
	;; -------- translation ------------

	(if (< (+ (redit-get-line-from-queue cc) xcc) (+ (redit-get-line-from-queue rc) xrc))
	    (if (= (redit-get-command-from-queue cc) redit-insert-command) (setq xrc (- xrc 1)))
	  (if (= (redit-get-command-from-queue cc) redit-delete-line-command) (setq xrc (- xrc 1))))

	(if (> (+ (redit-get-line-from-queue cc) xcc) (+ (redit-get-line-from-queue rc) xrc))
	    (if (= (redit-get-command-from-queue rc) redit-insert-command) (setq xcc (+ xcc 1)))
	  (if (= (redit-get-command-from-queue rc) redit-delete-line-command) (setq xcc (- xcc 1))))

	(if (= (+ (redit-get-line-from-queue cc) xcc) (+ (redit-get-line-from-queue rc) xrc))
	    (if (= (redit-get-command-from-queue rc) redit-insert-command) (setq xcc (+ xcc 1)))
	  (if (= (redit-get-command-from-queue rc) redit-replace-command)
	      (if (= (redit-get-command-from-queue cc) redit-insert-command) (setq xrc (+ xrc 1)))
	    (if (= (redit-get-command-from-queue cc) redit-replace-command) (setq cignore 1))
	    (if (= (redit-get-command-from-queue cc) redit-delete-line-command) (setq crc redit-insert-command) (setq cignore 1))))

	(if (= (redit-get-command-from-queue rc) redit-delete-line-command)
	    (if (= (redit-get-command-from-queue cc) redit-insert-command) (setq xrc (+ (redit-get-line-from-queue rc) 1)))
	  (if (= (redit-get-command-from-queue cc) redit-replace-command) (setq cignore 1))
	  (if (= (redit-get-command-from-queue cc) redit-delete-line-command) (setq cignore 1) (setq rignore 1)))

	;; -------- translation ------------

	(setq rque (cdr rque)))	;; while rque

      ;; ignore
      (if (= cignore 1)
	  ;; xxx
	  (setq cignore 0))
      (if (= rignore 1)
	  ;; xxx
	  (setq cignore 0))

      ;; -------- add after que ------------
      (enqueue after-CQ (concat (format "%2d%2d%9d" (redit-get-command-from-queue cc) (redit-get-uid-from-queue cc) (+ (redit-get-line-from-queue cc) xcc)) (redit-get-text-from-queue cc)))
      ;; -------- add after que ------------

      (setq cque (cdr cque))) ;; while cque
    )			      ;; let
  )			      ;; defun

(defun redit-line-translate-in (cque rque)
  "redit line translate for input"
  (let ((cc) (rc) (xcc) (xrc) (ccc) (crc) (cignore 0) (rignore 0))
    (setq xcc 0)
    (setq xrc 0)
    (setq cignore 0)
    (setq rignore 0)
    (while rque
      (setq rc (car rque))
      (setq cignore 0)
      (setq rignore 0)
      (while cque
	(setq cc (car cque))
	;; -------- translation ------------

	(if (and (= cignore 1) (= rignore 1))
	    (if (< (+ (redit-get-line-from-queue rc) xrc) (+ (redit-get-line-from-queue cc) xcc))
		(if (= (redit-get-command-from-queue rc) redit-insert-command) (setq xcc (+ xcc 1)))
	      (if (= (redit-get-command-from-queue rc) redit-delete-line-command) (setq xcc (- xcc 1))))

	  (if (> (+ (redit-get-line-from-queue rc) xrc) (+ (redit-get-line-from-queue cc) xcc))
	      (if (= (redit-get-command-from-queue cc) redit-insert-command) (setq xrc (- xrc 1)))
	    (if (= (redit-get-command-from-queue cc) redit-delete-line-command) (setq xrc (+ xrc 1))))

	  (if (= (+ (redit-get-line-from-queue rc) xrc) (+ (redit-get-line-from-queue cc) xcc))
	      (if (= (redit-get-command-from-queue cc) redit-insert-command) (setq xcc (+ xcc 1)))
	    (if (= (redit-get-command-from-queue cc) redit-replace-command)
		(if (= (redit-get-command-from-queue rc) redit-insert-command) (setq xcc (+ xcc 1)))
	      (if (= (redit-get-command-from-queue rc) redit-replace-command) (setq cignore 1))
	      (if (= (redit-get-command-from-queue rc) redit-delete-line-command) (setq crc redit-insert-command) (setq cignore 1))))
	  (if (= (redit-get-command-from-queue cc) redit-delete-line-command)
	      (if (= (redit-get-command-from-queue rc) redit-insert-command) (setq xcc (+ (redit-get-line-from-queue cc) 1)))
	    (if (= (redit-get-command-from-queue rc) redit-replace-command) (setq crc redit-insert-command) (setq cignore 1))
	    (if (= (redit-get-command-from-queue rc) redit-delete-line-command) (setq cignore 1) (setq rignore 1))))

	;; -------- translation ------------

	(setq cque (cdr cque)))	;; while rque

      ;; ignore
      (if (= cignore 1)
	  ;; xxx
	  (setq cignore 0))
      (if (= rignore 1)
	  ;; xxx
	  (setq rignore 0))

      ;; -------- add after que ------------
      (enqueue after-RQ (concat (format "%2d%2d%9d" (redit-get-command-from-queue rc) (redit-get-uid-from-queue rc) (+ (redit-get-line-from-queue rc) xrc)) (redit-get-text-from-queue rc)))
      ;; -------- add after que ------------

      (setq rque (cdr rque))) ;; while cque
    )			      ;; let
  )			      ;; defun

;; for debug
;;(defun redit-client-print-command-queue ()
;;  "Print command queue."
;;  (interactive)
;;  ;;(message redit-client-command-queue))
;;  (let (queue)
;;    (setq queue redit-client-command-queue)
;;    (while (Queue-front queue)
;;      (message (Queue-front queue))
;;      (setq queue (cdr (Queue-rear queue)))
;;      )))
;;
;;(defun redit-client-print-recive-queue ()
;;  "Print command queue."
;;  (interactive)
;;  (print redit-client-receive-queue))
;;
;;(defun redit-client-print-send-queue ()
;;  "Print command queue."
;;  (interactive)
;;  (print redit-client-send-queue))
;;