ansi-term で日本語表示

最近 Emacs の term-mode (M-x ansi-term) が (shell-mode に比べて) 便利で感動していたのだが、
日本語が正しく表示できない点が非常に残念だった。
が、先程

(setq locale-coding-system 'utf-8)

とすることで解決できた。
utf-8 じゃない環境では上の 'utf-8 を適切な coding-system に置き換えると良いと思います。

Google Desktop で howm の検索を(一部)高速化

4年程 howm を愛用している。
howm ファイルは1日1ファイルベースで、いまや1000個弱に近づいた。
ここ最近は、C-c , g や C-c , s での全文検索があまりに遅くて耐え難い。
遅いといっても10秒ちょっと待てば結果が出るのだが、多用するにはストレスが溜まる。
マシンのスペックを上げればいいのかもしれないが (現在 Pen4 3.2GHz, 1GB RAM)、
そう簡単にもいかないのが不況下の職場の辛いところ。

そんなときこそソフトウェアによる高速化だ!
まずは howm-view-use-grep を t にして、
検索を cygwingrep にやらせてみたけど、全然早くならねー
元々の fake-grep との違いがほとんど感じられない。これはだめだ。

もう少し調べてみると、namazu だの rast だのを導入すると早くなるっぽい。
けど、面倒そう。(ちゃんと調べてないので本当は簡単かも)

そこで、かなり昔から話題になっていた Google Desktop を導入してみた。
Google Desktopgrep 風に扱うための gdsgrep.rb なんてのもあるみたいだし、
これを howm から外部 grep として使わせれば楽勝!第三部完!
なんて軽い気持ちで取りくんでみたが、楽勝ではなかった。

まず、Google Desktop の検索は正規表現とか無理。(ですよね?)
さらに、ファイル内のキーワード出現位置(行数)とか教えてくれない。
これじゃ grep としての機能を果たせないよー

ということでメタヒューリスティック的に(合ってる?)
Google Desktop でファイル検索範囲を絞り込んでから、
普通に grep で検索をかけるので攻めてみた。

結果、正規表現は使えないけど、たいていの検索ではかなりの高速化が達成できたので、
以下に elisp を公開します。
たまに結果を取りこぼしたりしてるみたいだけど、
そこら辺はメタヒューリスティック(合ってる?)ということでご愛嬌

ちなみに WindowsMeadow 3 でしか試してません。
ブログのタイトル通り、何がおきても一切保証しません。
dolist のあたりとかぐちゃぐちゃですが気にしません。


2010-10-24 追記
上の記事は howm ファイルが Google デスクトップによって全文検索されることを前提として書いていますが、デフォルトの設定ではそうはならないかもしれません。
うまくいかない場合、howm ファイルの拡張子を .txt 等の全文検索対象拡張子に変えるか、Google デスクトップに Larry's Any Text File Indexer 拡張(テキストファイル全般を全文検索の対象にする拡張)をインストールする等してみてください。

;;; howm-gds.el --- howm with Google Desktop

;; Copyright (C) 2009  

;; Author:  yamdan <yamdan@gmail.com>
;; Keywords: howm, gds

;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; This file 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 General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.

;;; Commentary:

;; Setup:
;;
;;   Install Google Desktop, and add to your .emacs:
;;
;;   (require 'howm-gds)
;;   (setq howm-gds-search-default-dir "c:\\path\\to\\howm")
;;
;;   * edit "c:\\path\\to\\howm" to your howm directory.
;;

;;; Code:

(require 'howm)

(defvar howm-gds-search-url
  (if (functionp 'mw32-registry-get)
      (car-safe (mw32-registry-get "HKEY_CURRENT_USER\\Software\\Google\\Google Desktop\\API""search_url")))) 
(defvar howm-gds-search-default-dir "c:\\path\\to\\howm")
(defvar howm-gds-search-max-num 1000)
(defvar howm-gds-switch-original nil)  ; for debug


(defun howm-gds-call-view-search (regexp fixed-p &optional emacs-regexp)
  (let ((hilit (if emacs-regexp
                   `((,emacs-regexp . howm-view-hilit-face))
                 nil)))
    (howm-view-search-folder regexp
                             (howm-gds-search-path-folder regexp) ; 検索範囲を GDS で絞込
                             nil nil fixed-p hilit)
    (howm-record-view-window-configuration)))

(defun howm-gds-list-reminder (types name)
  (let* ((r (howm-reminder-regexp types))
         (rg (howm-reminder-regexp-grep types))
         (summarizer (howm-reminder-summarizer r t))
         (folder (howm-reminder-search-path-folder)))
    (howm-view-search-folder rg (howm-gds-search-path-folder rg)
                             name summarizer)
    (howm-list-exclude)
    (howm-reminder-add-font-lock)
    (howm-mode-add-font-lock)
    (let ((action-lock-default-rules
           (howm-action-lock-reminder-forward-rules t)))
      (action-lock-mode))))

(defun howm-gds-search-path-folder (regexp)
  (if howm-gds-switch-original
      (howm-search-path-folder)
    (let ((files (howm-gds-search-filepath regexp)))
      (if files
          (howm-make-folder:files files)
        (howm-search-path-folder) ; GDS による絞込に失敗したら、通常の検索範囲を設定
        ))))

(defun howm-gds-search-filepath (regexp &optional dir)
  (let* ((dir (or dir howm-gds-search-default-dir))
         (query (howm-gds-url-encode-string regexp 'utf-8))
         (in-dir (howm-gds-url-encode-string dir 'utf-8))
         (buf (url-retrieve-synchronously
               (concat
                howm-gds-search-url
                "&ie=utf-8"
                "&adv=1"
                "&has=" query
                "&type=cat_files"
                "&num=" (number-to-string howm-gds-search-max-num)
                "&in=" in-dir
                "&under=on"
                "&format=xml")))
         filepath-list)
    (save-excursion
      (set-buffer buf)
      (let* ((results-set (xml-parse-region (point-min) (point-max))))
        (dolist (results-elt results-set)
          (and (listp results-elt) (equal (car results-elt) 'results)
               (dolist (result-elt (xml-node-children results-elt))
                 (and (listp result-elt) (equal (car result-elt) 'result)
                      (let* ((url-elt (assoc 'url (xml-node-children result-elt)))
                             (url (and (listp url-elt) (car-safe (xml-node-children url-elt)))))
                        (and url (add-to-list 'filepath-list url)))))))))
    filepath-list))

;; refer to w3m-url-encode-string (in w3m.el)
(defun howm-gds-url-encode-string (str &optional coding)
  (mapconcat
   (lambda (ch)
     (cond
      ((eq ch ?\n)                      ; newline
       "%0D%0A")
      ((string-match "[-a-zA-Z0-9_:/.]" (char-to-string ch)) ; xxx?
       (char-to-string ch))             ; printable
      ((char-equal ch ?\x20)            ; space
       "+")
      (t
       (format "%%%02X" ch))))          ; escape
   ;; Coerce a string into a list of chars.
   (append (encode-coding-string (or str "")
                                 (or coding
                                     'iso-2022-jp))
           nil)
   ""))

(defalias 'howm-call-view-search 'howm-gds-call-view-search)
(defalias 'howm-list-reminder 'howm-gds-list-reminder)


(provide 'howm-gds)
;;; howm-gds.el ends here

おっと wanderlust と gmail の悪口はそこまでだ

久しぶりに wanderlustgmail を読もうとしたら、

Cannot open: elmo-network-initialize-session

なんてエラーが表示され、メールが取得できなくなってしまっていた。

色々調べる内に、原因と対策が判明したので、以下に記録する。

以下のページがとても役に立った。感謝します。

[riwablo: さくらインターネットimapとopensslとwl]
http://riwablo.blogspot.com/2008/04/imapopensslwl.html

原因

imap.gmail.comSSL/TLS で接続する際、
imap.gmail.comサーバ証明書の検証に失敗するため、
wanderlust (elmo) がセッション確立の途中でエラーを吐いて終了する。

サーバ証明書の検証に失敗してしまうのは、
証明書の発行者 (issuer) である "Equifax Secure Certificate Agency" のルート証明書が、
openssl から利用できていないため。

対策(1) (推奨)

openssl 用証明書ストア (ディレクトリ) の準備

openssl が証明書を読みにいくディレクトリを用意する。

bash-3.2$ mkdir -m 744 -p ~/.w3/certs
bash-3.2$
ルート証明書の取得

openssl s_client で imap.gmail.com:993 へ接続してみると、
imap.gmail.comサーバ証明書は Equifax Secure Certificate Agency によって
発行されていることが分かる。

bash-3.2$ openssl s_client -host imap.gmail.com -port 993
CONNECTED(00000006)
depth=1 /C=US/O=Google Inc/CN=Google Internet Authority
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=imap.gmail.com
i:/C=US/O=Google Inc/CN=Google Internet Authority
1 s:/C=US/O=Google Inc/CN=Google Internet Authority
i:/C=US/O=Equifax/OU=Equifax Secure Certificate Authority
(以下略)

Equifax Secure Certificate Agency のルート証明書は、
firefox の証明書ストアなどに入っているので、そこから取得する。

環境設定→詳細→暗号化→証明書を表示...
で表示される証明書のリストから
"Equifax" の "Equifax Secure CA" をクリックし、
"書き出す..." ボタンをクリック。

ファイル形式は PEM を選択し、
上で作った ~/.w3/certs ディレクトリへ保存する。

シンボリックリンクの生成

openssl が適切に証明書を選択できるように、
subject のハッシュ値に ".0" を付加したシンボリックリンクを作る。
といってもやるのはコマンド c_rehash を実行するだけ。

bash-3.2$ c_rehash ~/.w3/certs
bash-3.2$

これで ~/.w3/certs 内に証明書へのシンボリックリンクが作られる。

bash-3.2$ ls -l ~/.w3/certs
total 16
lrwxr-xr-x  1 yamdan  staff    19  4  7 00:09 594f1775.0 -> EquifaxSecureCA.pem
-rw-r--r--@ 1 yamdan  staff  1162  4  7 00:06 EquifaxSecureCA.pem
.wl の編集

.wl へ以下を追記する。

;; SSL/TLS 用証明書ストアのパス
(setq ssl-certificate-directory "/Users/yamdan/.w3/certs")

ここでは必ず絶対パスを指定すること。
"~/.w3/certs" のような指定をするとディレクトリの読み込みに失敗する。

(ssl.el の内部では、openssl の実行に
start-process-shell-command ではなく start-process を利用しており、
シェルによる引数の展開 "~/.w3/certs" --> "/Users/yamdan/.w3/certs"
が行われないため)

完了

以上で imap.gmail.com への接続に成功するはず。
.wl は最終的に以下のようになる。

;; IMAP サーバの設定
(setq elmo-imap4-default-server "imap.gmail.com")
(setq elmo-imap4-default-user "xxxxxxxxxxxx@gmail.com")
(setq elmo-imap4-default-authenticate-type 'clear)
(setq elmo-imap4-default-port '993)
(setq elmo-imap4-default-stream-type 'ssl)
(setq elmo-imap4-use-modified-utf7 t)

;; SMTP サーバの設定
(setq wl-smtp-connection-type 'starttls)
(setq wl-smtp-posting-port 587)
(setq wl-smtp-authenticate-type "plain")
(setq wl-smtp-posting-user "xxxxxxxxxxxxx")
(setq wl-smtp-posting-server "smtp.gmail.com")

;; SSL/TLS 用証明書ストアのパス
(setq ssl-certificate-directory "/Users/yamdan/.w3/certs")

対策(2) (非推奨)

上の手順の代わりに、

(setq ssl-certificate-verification-policy 1)

のように、ssl-certificate-verification-policy の値に 0 以外を与えても、エラーが抑制される。
証明書の検証に失敗しているのには変わらないはずなんだが、
この挙動の違いはなんなのだろう?

ssl.el の中を見ると、ssl-certificate-verification-policy の値は、
openssl s_client の -verify オプションの値として利用されるようだ。

そしてどうも openssl s_client は
「-verify 0 オプションを付加したときだけ」
証明書検証失敗時の出力が変化するようだ。

bash-3.2$ openssl s_client -host imap.gmail.com -port 993 -verify 0 -CApath ~/.w3/certs
verify depth is 0
CONNECTED(00000003)
depth=1 /C=US/O=Google Inc/CN=Google Internet Authority
verify error:num=20:unable to get local issuer certificate
verify return:0
49965:error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed:s3_clnt.c:983:

-verify 0 オプションを付加したときだけは、
openssl s_client は error を吐いて途中終了する。
この error が ssl.el 経由で elmo へ伝わり、

Cannot open: elmo-network-initialize-session

のエラー出力へ繋がっていたようだ。

一方、-verify オプションを付加しない場合や、付加しても 0 以外の値を与えた場合には、
証明書検証に失敗しようが問答無用で、セッションの確立までは進んでしまう。

bash-3.2$ openssl s_client -host imap.gmail.com -port 993 -verify 1 -CApath ~/.w3/certs
verify depth is 1
CONNECTED(00000003)
depth=1 /C=US/O=Google Inc/CN=Google Internet Authority
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=1 /C=US/O=Google Inc/CN=Google Internet Authority
verify error:num=27:certificate not trusted
verify return:1
depth=0 /C=US/ST=California/L=Mountain View/O=Google Inc/CN=imap.gmail.com
verify return:1
---
Certificate chain
 0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=imap.gmail.com
   i:/C=US/O=Google Inc/CN=Google Internet Authority
 1 s:/C=US/O=Google Inc/CN=Google Internet Authority
   i:/C=US/O=Equifax/OU=Equifax Secure Certificate Authority
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIDLzCCApigAwIBAgIKKNVg8QAAAAAHfzANBgkqhkiG9w0BAQUFADBGMQswCQYD
VQQGEwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzEiMCAGA1UEAxMZR29vZ2xlIElu
dGVybmV0IEF1dGhvcml0eTAeFw0wOTAzMTIyMDIzNDFaFw0xMDAzMTIyMDMzNDFa
MGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1N
b3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgSW5jMRcwFQYDVQQDEw5pbWFw
LmdtYWlsLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAo1cDhDhgSN3M
9Do6D459nSNxZW1fTPJV5rmJctt12zZwDTBbHqWlZsK1ZvcV1ngj1nYgZ7RXZmgC
sxEUSK/sw+/5Otl6BpK3OLFZI5U/FmMUTssG588Hsl21SBxeK7RoAgxxM2vB2Jgi
3KH+E13mlgUXlCyrehnbbTlwEosURyUCAwEAAaOCAQAwgf0wHQYDVR0OBBYEFNiX
w7Ps6PWzF80VrxYbz63ucVArMB8GA1UdIwQYMBaAFL/AMOv1QxE+Z7qekfv8atrj
axIkMEYGA1UdHwQ/MD0wO6A5oDeGNWh0dHA6Ly9jcmwuZXh0Lmdvb2dsZS5jb20v
R29vZ2xlSW50ZXJuZXRBdXRob3JpdHkuY3JsMFAGCCsGAQUFBwEBBEQwQjBABggr
BgEFBQcwAoY0aHR0cDovL2NhLmV4dC5nb29nbGUuY29tL0dvb2dsZUludGVybmV0
QXV0aG9yaXR5LmNydDAhBgkrBgEEAYI3FAIEFB4SAFcAZQBiAFMAZQByAHYAZQBy
MA0GCSqGSIb3DQEBBQUAA4GBALo7fCzccb+0OeU+iGPJ/CEynTIegiqt/OkZPP4P
aVy7a0MjwK7mDDYpMMu10r4oYSM8iI1BN2iIXRFDkuogp48yeh8P3In1A332XdW3
UoaoIhI8DunLXgfBEr2/TPUHtXDfJumu+wCojdjT2ywwud1hY8LSRR2zv27FWEAD
OaRh
-----END CERTIFICATE-----
subject=/C=US/ST=California/L=Mountain View/O=Google Inc/CN=imap.gmail.com
issuer=/C=US/O=Google Inc/CN=Google Internet Authority
---
No client certificate CA names sent
---
SSL handshake has read 1660 bytes and written 306 bytes
---
New, TLSv1/SSLv3, Cipher is RC4-MD5
Server public key is 1024 bit
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : RC4-MD5
    Session-ID: xxxxxxxx
    Session-ID-ctx: 
    Master-Key: xxxxxxxx
    Key-Arg   : None
    Start Time: 1241109692
    Timeout   : 300 (sec)
    Verify return code: 27 (certificate not trusted)
---
* OK Gimap ready for requests from xxx.xxx.xxx.xxx

途中で verify error とか certificate not trusted とか出てるけど、
セッションは確立する。
elmo や wanderlust はこれをエラーと認識できないようだ。

ちなみに openssl のバージョンは 0.9.8i です。

bash-3.2$ openssl version
OpenSSL 0.9.8i 15 Sep 2008

結論?

openssl の動きが把握できていないため確信が持てない部分があるものの、
とりあえず上の対策(1)の通り、
ssl-certificate-verification-policy-verify はデフォルト値 (0) のままで、
~/.w3/certs 内に Equifax Secure Certificate Authority のルート証明書
を入れておく方法が安全策ではないだろうか。

meadow 3 の python-mode

デフォルトのpython-modeだと、どうも補完がうまくいかない。

http://www.emacswiki.org/cgi-bin/wiki/PythonMode#toc2
に、

The version in Emacs 22 has a bunch of problems. Use

    * http://www.loveshack.ukfsn.org/emacs/python.el It requires
    * http://www.loveshack.ukfsn.org/emacs/emacs.py and
    * http://www.loveshack.ukfsn.org/emacs/sym-comp.el

とあるので、それぞれダウンロードして、
python.el を %MEADOW%\lisp\progmodes 下へ、
emacs.py を %MEADOW%\etc 下へ、
sym-comp.el を %MEADOW%\lisp 下へ、
それぞれコピーしたら解決した。
sym-comp.el は site-lisp の方がいいのかな?