[Date Prev][Date Next][Thread Prev][][Date Index][Thread Index]

Re: Current status of backend patch



Citation (with leading "> " of each line) from article:
  <20010326002244R.1000@pine.kuee.kyoto-u.ac.jp>
    by TSUCHIYA Masatoshi <tsuchiya@pine.kuee.kyoto-u.ac.jp> :
> [*] 多言語化 w3m.
>     詳細については http://pub.ks-and-ks.ne.jp/prog/w3mmee/ を参照。

えーと「多言語化」とすると誤解を招きそうです。私がやっているのは可能な
限り沢山の文字の符号化を同時に扱えるようにするだけで、あまり「言語」を
扱っているという意識はないので。また「多言語化w3m」という場合は、坂本
氏の

  Multilingualization of w3m (w3m-m17n):
  http://www2u.biglobe.ne.jp/~hsaka/w3m/index.html

を指すことになっています。

> その最初の成果として、改良された backend patch が [w3m-dev 01827] に投

バタバタして申し訳無いですが、早速バグフィックスパッチを2個投稿してい
ます。…などと言っているうちに、showProgress中の端末の有無のチェックが
甘くてsegmentation faultするというバグが発覚したので^^;、これからまた
修正します。

> ## このパッチって、どの枝の差分なんでしょう? > 須藤さん

オリジナル用の枝はw3m-0_2です。オリジナルそのものにはw3m-0_2_1というタ
グが振ってあります。

> (1) head / get コマンドで redirect 先の実際の URI が表示されるようにし
>     て欲しい。
>
>     ex) http://www-nagao.kuee.kyoto-u.ac.jp/member/tsuchiya/emacs-w3m/
>         -> http://namazu.org/~tsuchiya/emacs-w3m/

擬似(?)ヘッダとして

  w3m-current-url: http://namazu.org/~tsuchiya/emacs-w3m/

のように出力する、というのでよろしいでしょうか。

> (2) <select> タグを処理できないでしょうか。例えば、以下のような HTML 
>     を -halfdump すると、

selectの候補はform_list構造体の中に記録されているのでhalfdump imageを
作る途中で出力するのは難しそうです。最後のtitleの後に出力される_formの
中で

  <_form fid=… option_label=… option_value=… …>

という形で出力する、というのでは如何ですか。

ところで前に書いたbackend版w3m.elですが、↓に添付しますので何かに使っ
ていただけるとありがたいです。非同期にbackend modeのw3mと通信する部分
だけで、まったくテストしていないので申し訳無いんですが。

-- 
須藤 清一 <suto@ks-and-ks.ne.jp>
http://pub.ks-and-ks.ne.jp/pgp-public-key.html
;;; Handle process asynchronously

(defconst w3m-backend-process-name "w3m-backend")
(defconst w3m-backend-prompt-regex "^w3m>\s *\\'")
(defconst w3m-backend-buffer-name " *w3m-backend*")
(defvar w3m-backend-request-head nil)
(defvar w3m-backend-request-tail nil)
(defvar w3m-backend-result-beginning (make-marker))
(defvar w3m-backend-result-nextline (make-marker))
(defvar w3m-backend-result-length nil)
(defvar w3m-backend-input-prompt nil)
(defvar w3m-backend-input-default nil)
(defvar w3m-backend-status 'dead)
(defvar w3m-backend-receiver nil)
(defvar w3m-backend-url nil)
(defvar w3m-backend-sync-box nil)
(defvar w3m-backend-result-hook nil)
(make-variable-buffer-local 'w3m-backend-result-hook)

(defun w3m-backend-arg-to-string (arg)
  (if (stringp arg)
      arg
    (print1-to-string (eval arg))))

(defun w3m-backend-start-process (&rest requests)
  "Start w3m-command in backend mode."
  (save-excursion
    (set-buffer (w3m-get-buffer-create w3m-backend-buffer-name))
    (erase-buffer)
    (setq w3m-backend-request-head nil
	  w3m-backend-request-tail nil
	  w3m-backend-status nil
	  w3m-backend-receiver nil)
    (let* ((coding-system-for-read 'binary)
	   (coding-system-for-write w3m-coding-system)
	   (default-process-coding-system (cons 'binary w3m-coding-system))
	   (process-connection-type w3m-process-connection-type)
	   (proc (apply (function start-process)
			w3m-backend-process-name
			(current-buffer)
			w3m-command
			(mapcar (function w3m-backend-arg-to-string) w3m-backend-arguments))))
      (if proc
	  (progn (set-process-filter proc (function w3m-backend-filter))
		 (set-process-sentinel proc (function w3m-backend-sentinel))
		 (apply (function w3m-backend-process-request) proc (current-buffer) requests)))
      proc)))

(defun w3m-backend-process-send-string (proc req)
  (let ((b (point-max)) e)
    (goto-char b)
    (insert req)
    (setq e (point))
    (if (< b e)
	(progn (backward-char)
	       (if (char-equal (char-after (point)) ?\n)
		   (progn (goto-char e) (insert "\n"))
		 (goto-char e)))
      (insert "\n"))
    (setq w3m-backend-status 'head
	  w3m-backend-input-prompt nil
	  w3m-backend-input-default nil
	  w3m-backend-result-length 0)
    (process-send-region proc b (point))
    (move-marker w3m-backend-result-nextline (point))
    (move-marker w3m-backend-result-beginning (point))))

(defun w3m-backend-process-request (proc buf &rest requests)
  (cond ((and (not proc) (eq w3m-backend-status 'dead))
	 (apply (function w3m-backend-start-process) requests))
	((and (or proc
		  (setq proc (get-process w3m-backend-process-name)))
	      (or buf
		  (setq buf (process-buffer proc))))
	 (let (req)
	   (while requests
	     (setq req (list (car requests))
		   requests (cdr requests))
	     (if w3m-backend-request-tail
		 (progn (setcdr w3m-backend-request-tail req)
			(setq w3m-backend-request-tail req))
	       (setq w3m-backend-request-head req
		     w3m-backend-request-tail req)))
	   (unless w3m-backend-sync-box
	     (save-excursion
	       (set-buffer buf)
	       (goto-char (point-max))
	       (if (and w3m-backend-request-head
			(not w3m-backend-status)
			(re-search-backward w3m-backend-prompt-regex (point-min) t))
		   (progn (setq req (car w3m-backend-request-head)
				w3m-backend-request-head (cdr w3m-backend-request-head))
			  (unless w3m-backend-request-head
			    (setq w3m-backend-request-tail nil))
			  (setq w3m-backend-receiver (nth 0 req)
				w3m-backend-url (nth 1 req)
				req (nth 2 req))
			  (cond ((stringp req)
				 (w3m-backend-process-send-string proc req))
				((and req (listp req))
				 (w3m-backend-process-send-string
				  proc
				  (apply (function concat)
					 (w3m-backend-arg-to-string (car req))
					 (mapcar
					  (function (lambda (arg) (concat " " (w3m-backend-arg-to-string arg))))
					  (cdr req)))))
				((functionp req) (funcall req proc buf)))))))))))

(defun w3m-backend-process-sync-request (proc buf url req)
  (and (or proc
	   (setq proc (if (eq w3m-backend-status 'dead)
			  (funcall (function w3m-backend-start-process))
			(get-process w3m-backend-process-name))))
       (let ((box (list)))
	 (w3m-backend-process-request
	  proc buf
	  (list (` (lambda (b e)
		     (setcdr (quote (, box)) (list b e))
		     (setcar (quote (, box)) (current-buffer))
		     (setq w3m-backend-sync-box (quote (, box)))))
		url seq))
	 (while (and (eq (process-status proc) 'run) (not (eq w3m-backend-sync-box box)))
	   (accept-process-output proc))
	 (setq w3m-backend-sync-box nil)
	 box)))

(defun w3m-backend-filter (proc str)
  (if (buffer-name (process-buffer proc))
      (with-current-buffer (process-buffer proc)
	(let ((buffer-read-only nil)
	      (case-fold-search nil))
	  (goto-char (process-mark process))
	  (insert str)
	  (cond ((eq w3m-backend-status 'head)
		 (let ((continuep (string< "" str)))
		   (while continuep
		     (goto-char (marker-position w3m-backend-result-nextline))
		     (cond ((looking-at "w3m-content-length:[ \t]*\\([0-9]+\\).*\n")
			    (setq w3m-backend-result-length
				  (string-to-number
				   (buffer-substring (match-beginning 1) (match-end 1))))
			    (move-marker w3m-backend-result-nextline (match-end 0)))
			   ((looking-at "w3m-urgent-messages:[ \t]*\\(.*\\)\n")
			    (w3m-message (buffer-substring (match-beginning 1) (match-end 1)))
			    (delete-region (match-beginning 0) (match-end 0)))
			   ((looking-at "w3m-input-prompt:[ \t]*\\(.*\\)\n")
			    (setq w3m-backend-input-prompt (buffer-substring (match-beginning 1) (match-end 1)))
			    (delete-region (match-beginning 0) (match-end 0)))
			   ((looking-at "w3m-input-default:[ \t]*\\(.*\\)\n")
			    (setq w3m-backend-input-default (buffer-substring (match-beginning 1) (match-end 1)))
			    (delete-region (match-beginning 0) (match-end 0)))
			   ((looking-at "w3m>.*\\'")
			    (if w3m-backend-input-prompt
				(progn (delete-region (match-beginning 0) (match-end 0))
				       (cond ((string-match "\\<Password:" w3m-backend-input-prompt)
					      (setq w3m-process-passwd
						    (or (nth 1 (w3m-exec-get-user w3m-backend-url))
							(read-passwd w3m-backend-input-prompt)))
					      (condition-case nil
						  (process-send-string proc (concat w3m-process-passwd "\n"))
						(error nil)))
					     ((string-match "\\<Username:" w3m-backend-input-prompt)
					      (setq w3m-process-user
						    (or (nth 0 (w3m-exec-get-user w3m-backend-url))
							(read-from-minibuffer w3m-backend-input-prompt w3m-backend-input-default)))
					      (condition-case nil
						  (process-send-string proc (concat w3m-process-user "\n"))
						(error nil)))
					     ((string< "" w3m-backend-input-prompt)
					      (condition-case nil
						  (process-send-string
						   proc
						   (concat
						    (read-from-minibuffer w3m-backend-input-prompt w3m-backend-input-default)
						    "\n"))
						(error nil)))
					     (t
					      (setq w3m-backend-status nil
						    continuep nil)
					      (w3m-backend-process-request proc nil))))
			      (w3m-backend-call-receiver (match-beginning 0))))
			   ((looking-at "\\(.*\\)\n")
			    (move-marker w3m-backend-result-nextline (match-end 0))
			    (if (= (match-beginning 1) (match-end 1))
				(setq w3m-backend-status 'body)))
			   (t (setq continuep nil))))))
		((eq w3m-backend-status 'body)
		 (if (>= (- (point) (marker-position w3m-backend-result-nextline)) w3m-backend-result-length)
		     (w3m-backend-call-receiver
		      (+ (marker-position w3m-backend-result-nextline) w3m-backend-result-length)))))))))

(defun w3m-backend-call-receiver (e)
  (let ((b (marker-position w3m-backend-result-beginning)))
    (cond ((bufferp w3m-backend-receiver)
	   (let ((cur (current-buffer)) hb he)
	     (set-buffer w3m-backend-receiver)
	     (setq hb (point))
	     (insert-buffer-substring cur b e)
	     (if (functionp w3m-backend-result-hook)
		 (funcall w3m-backend-result-hook hb (point)))
	     (set-buffer cur)))
	  ((functionp w3m-backend-receiver)
	   (funcall w3m-backend-receiver b e)))
    (setq w3m-backend-status nil)
    (delete-region (point-min) e)))

(defun w3m-backend-sentinel (proc event)
  (if (string-match "^finished\\|^exited" event)
      (setq w3m-backend-status 'dead)))


;;; Retrieve data via HTTP:

(defun w3m-async-cache-contents (url buffer &optional beg end)
  "Store URL's contents which is placed in the BUFFER.
Return symbol to identify its cache data."
  (w3m-cache-setup)
  (let ((ident (intern url w3m-cache-hashtb)))
    (w3m-cache-remove url)
    ;; Remove the oldest article, if necessary.
    (and (numberp w3m-keep-cache-size)
	 (>= (length w3m-cache-articles) w3m-keep-cache-size)
	 (w3m-cache-remove-oldest))
    ;; Insert the new article.
    (or (and beg end)
	(save-excursion
	  (set-buffer buffer)
	  (or beg (setq beg (point-min)))
	  (or end (setq end (point-max)))))
    (save-excursion
      (set-buffer w3m-cache-buffer)
      (let (buffer-read-only)
	(goto-char (point-max))
	(unless (bolp) (insert "\n"))
	(let ((b (point)))
	  (insert-buffer-substring buffer beg end)
	  ;; Tag the beginning of the article with the ident.
	  (when (> (point-max) b)
	    (put-text-property b (1+ b) 'w3m-cache ident)
	    (setq w3m-cache-articles (cons ident w3m-cache-articles))
	    ident))))))

(defun w3m-async-cache-request-contents (url &optional buffer)
  "Insert URL's data to the BUFFER if BUFFER is a buffer,
or passed to BUFFER if BUFFER is a function.
If URL's data is found in the cache, return t.  Otherwise return nil.
When BUFFER is nil, all data will be inserted in the current buffer."
  (w3m-cache-setup)
  (let ((ident (intern url w3m-cache-hashtb)))
    (when (memq ident w3m-cache-articles)
      ;; It was in the cache.
      (let (beg end type charset)
	(save-excursion
	  (set-buffer w3m-cache-buffer)
	  (if (setq beg (text-property-any
			 (point-min) (point-max) 'w3m-cache ident))
	      ;; Find the end (i. e., the beginning of the next article).
	      (setq end (next-single-property-change
			 (1+ beg) 'w3m-cache (current-buffer) (point-max)))
	    ;; It wasn't in the cache after all.
	    (setq w3m-cache-articles (delq ident w3m-cache-articles))))
	(and beg
	     end
	     (save-excursion
	       (let (buffer-read-only)
		 (cond ((functionp buffer)
			(funcall buffer w3m-cache-buffer beg end))
		       ((buffep buffer)
			(set-buffer buffer)
			(insert-buffer-substring w3m-cache-buffer beg end))
		       (t
			(insert-buffer-substring w3m-cache-buffer beg end))))
		 t))))))

(defun w3m-w3m-async-attributes-internal (cbuf cb ce)
  (let (type charset len enc time id)
    (save-excursion
      (set-buffer cbuf)
      (goto-char cb)
      (let ((continuep t) name)
	(while (and continuep (< (point) ce))
	  (cond ((char-equal (char-after) ?\n) (setq continuep nil))
		((looking-at "\\([^:\n]+\\):[ \t] *\\(.*\\)\n")
		 (setq name (intern-soft (downcase (buffer-substring (match-beginning 1) (match-end 1)))))
		 (cond ((eq name 'w3m-content-type)
			(setq type (buffer-substring (match-beginning 2) (match-end 2))))
		       ((eq name 'w3m-content-charset)
			(setq charset (buffer-substring (match-beginning 2) (match-end 2))))
		       ((eq name 'w3m-content-length)
			(setq len (string-to-number (buffer-substring (match-beginning 2) (match-end 2)))))
		       ((eq name 'content-encoding)
			(setq enc (buffer-substring (match-beginning 2) (match-end 2))))
		       ((eq name 'last-modified)
			(setq time (apply (function encode-time)
					  (w3m-time-parse-string (buffer-substring (match-beginning 2) (match-end 2))))))
		       ((eq name 'w3m-buffer-id)
			(setq id (buffer-substring (match-beginning 2) (match-end 2)))))
		 (goto-char (match-end 0)))
		(t (re-search-forward "\n" ce 1))))
	(list type charset len enc time id)))))

(defun w3m-w3m-async-attributes (receiver url &optional no-cache)
  "Pass url and a list of attributes of URL to RECEIVER.
The list is nil if retirieval of header is failed.  Otherwise,
list elements are:
 0. Type of contents.
 1. Charset of contents.
 2. Size in bytes.
 3. Encoding of contents.
 4. Last modification time.
 5. Identifier in w3m of buffer.
If optional argument NO-CACHE is non-nil, cache is not used."
  (or (and (not no-cache)
	   (w3m-cache-request-contents
	    url
	    (` (lambda (cbuf cb ce)
		 (funcall (function (, receiver))
			  (quote (, url))
			  (w3m-w3m-async-attributes-internal cbuf cb ce))))))
      (w3m-backend-process-request
       nil nil
       (` (lambda (b e)
	    (w3m-cache-contents (quote (, url)) (current-buffer) b e)
	    (funcall (function (, receiver))
		     (quote (, url))
		     (w3m-w3m-async-attributes-internal (current-buffer) b e)))
	  (, url)
	  (, (concat "get " url))))))

(defun w3m-w3m-async-retrieve-internal (receiver url no-decode buf b e)
  (let* ((headers (w3m-w3m-async-attributes-internal buf b e))
	 (type    (car headers))
	 (charset (nth 1 headers))
	 (length  (nth 2 headers))
	 (bob (save-excursion
		(set-buffer buf)
		(goto-char b)
		(re-search-forward "^\n" e 1)
		(point))))
    (w3m-with-work-buffer
      (delete-region (point-min) (point-max))
      (set-buffer-multibyte nil)
      (insert-buffer-substring buf bob e)
      (when (string= "text/html" type)
	;; Remove cookies.
	(goto-char (point-min))
	(while (and (not (eobp))
		    (looking-at "Received cookie: "))
	  (forward-line 1))
	(skip-chars-forward " \t\r\f\n")
	(if (or (looking-at "<!DOCTYPE")
		(looking-at "<HTML>"))	; for eGroups.
	    (delete-region (point-min) (point))))
      (and (string-match "^text/" type)
	   (not no-decode)
	   (w3m-decode-buffer type charset))
      (funcall receiver type))))

(defun w3m-w3m-async-retrieve (receiver url &optional no-decode no-cache)
  "Retrieve content of URL with w3m, insert it to the working buffer,
and then call RECEIVER with content-type of URL as string. If NO-DECODE,
set the multibyte flag of the working buffer to nil."
  (or (and (not no-cache)
	   (w3m-cache-request-contents
	    url
	    (` (lambda (cbuf cb ce)
		 (w3m-w3m-async-retrieve-internal
		  (function (, receiver)) (quote (, url)) (quote (, no-decode))
		  cbuf cb ce)))))
      (w3m-backend-process-request
       nil nil
       (` (lambda (b e)
	    (w3m-cache-contents (quote (, url)) (current-buffer) b e)
	    (w3m-w3m-async-retrieve-internal
	     (function (, receiver)) (quote (, url)) (quote (, no-decode))
	     (current-buffer) b e))
	  (, url)
	  (, (concat "get " url))))))

(defun w3m-async-retrieve (receiver url &optional no-decode no-cache)
  "Retrieve content of URL and insert it to the working buffer.
This function will pass content-type of URL as string to RECEIVER
when retrieval succeed.  If NO-DECODE, set the multibyte flag of
the working buffer to nil."
  (cond
   ((string-match "^about:" url)
    (let (func)
      (if (and (string-match "^about://\\([^/]+\\)/" url)
	       (setq func (intern-soft
			   (concat "w3m-about-" (match-string 1 url))))
	       (fboundp func))
	  (funcall func receiver url no-decode no-cache)
	(w3m-about receiver url no-decode no-cache))))
   ((string-match "^cid:"; url)
    (let ((func (cdr (assq major-mode w3m-cid-retrieve-function-alist))))
      (when func
	(funcall func receiver url no-decode no-cache))))
   (t
    (w3m-w3m-async-retrieve receiver url no-decode no-cache))))

(defun w3m-async-download-internal (type url filename)
  (if type
      (let ((buffer-file-coding-system 'binary)
	    (file-coding-system 'binary)
	    (coding-system-for-write 'binary)
	    jka-compr-compression-info-list
	    jam-zcat-filename-list
	    format-alist)
	(if (or (not (file-exists-p filename))
		(y-or-n-p (format "File(%s) is already exists. Overwrite? " filename)))
	    (write-region (point-min) (point-max) filename)))
    (error "Unknown URL: %s" url)))

(defun w3m-async-download (url &optional filename no-cache)
  (unless filename
    (setq filename (w3m-read-file-name nil nil url)))
  (w3m-async-retrieve
   (` (lambda (type) (w3m-download-internal type (quote (, url)) (quote (, filename)))))
   t no-cache))

(defun w3m-async-attributes (receiver url &optional no-cache)
  "Pass a list of attributes of URL to RECEIVER.
Value is nil if retirieval of header is failed.  Otherwise, list
elements are:
 0. Type of contents.
 1. Charset of contents.
 2. Size in bytes.
 3. Encoding of contents.
 4. Last modification time.
 5. Identifier of buffer in w3m.
If optional argument NO-CACHE is non-nil, cache is not used."
  (cond
   ((string-match "^about:" url)
    (funcall receiver (list "text/html" nil nil nil nil)))
   (t
    (w3m-w3m-async-attributes receiver url no-cache))))

(provide 'w3m-async)