Skip to content
2010/08/23 / highmt

cygwin 1.7.6 + lgrep 4.51 on NTEmacs 23.2

CYGWIN_NT-6.0 z90ns-5ff7df0e 1.7.6(0.230/5/3) 2010-08-16 16:06 i686 Cygwin
GNU Emacs 23.2.1 (i386-mingw-nt6.0.6002)
lv v.4.51 (Jan.16th,2004)

ついに git 1.6.x での取得が失敗するリポジトリが出てきたため、
cygwin 1.5.25をあきらめてcygwin 1.7.x へ移行することに。

cygwinかemacsのバージョンをあげると、だいたいemacsとcygwinまわりをうまく動かすのがひと仕事になる。
今回も、かなり強引に動くようにした。

とりあえず以前の設定のまま動かしてみる

default-process-coding-systemは、

(setq default-process-coding-system '(japanese-shift-jis-dos . japanese-shift-jis-unix))

この設定にあまり根拠はがあるわけではなく、もともと「utf8 cygwin + lgrep on Meadow」でそういう設定にしていたため。
不都合があれば後で変える。

利用するシェルは、

(setq shell-file-name "sh")
(setq shell-command-switch "-c")

これも以前のまま。

grepの設定は、

(setq grep-use-null-device nil)
(setq grep-command "lgrep -Ku8 -Ou8 -n '' {*,.*}")
(setq grep-template "lgrep  -Ku8 -Ou8 -n '' ")
(setq grep-find-command '("find . -type f -exec lgrep -Ku8 -Ou8 -n '' {} NUL \\;" . 42))
(defadvice grep (around grep-coding-setup activate)
  (let ((coding-system-for-read 'utf-8))
    ad-do-it))

cygwinの環境変数は、

  • CYGWIN=winsymlinks nodosfilewarning
  • LC_TYPE=ja_JP.UTF-8

この状態で動かすと、英数字での検索は特に問題なく動く。
でも、日本語を入れると、

lgrep -Ku8 -Ou8 -n '日本語' {*,.*}
/usr/bin/sh: $'lgrep -Ku8 -Ou8 -n \'\346\227\245\346\234\254\350\252\236\' {*,.*}': command not found

こんな感じで動きません。 (2010/09/26:設定に誤りが… 下参照。)

ちなみに、$’…’というのは、\nnnで文字を表わすための書式らしい。man shとかで出てくる。

とりあえずgrepとfind-grepで日本語が通るように

まずは、shの引数として何が渡されているのか調べることに。
適当な無限ループするsh.exeをつくって置き換えてみて、タスクマネージャで起動時の引数を確認する。
(ProcessExplorerでも確認できるはずなのだが、自分の環境では、空が表示されてしまって確認できなかった。)

そうすると、

C:\cygwin\bin\sh.exe -c "lgrep -Ku8 -Ou8 -n '日本語' {*,.*}"

とそのままの感じで起動しているっぽい。
これで動かない、ということは、shがうまく引数を扱えていない、ということになる。

というわけで、引数を受け取ってsystemに流すだけのfakeshをつくってどういう状況か様子を見ることに。
最終形は以下のような感じになった。

fakesh.c:

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <alloca.h>
#include <string.h>

#ifdef DEBUG
/*
	example: LOG((debug, "[%s]", message))
 */
#define LOG(msg) \
{ \
	FILE *debug = fopen("c:/cygwin/var/log/fakesh.log", "ab"); \
	(fprintf msg); \
	fclose(debug); \
}
#else
#define LOG(msg)
#endif

int strcpy_unescape(char *newstr, const char * str, int len) {
	int i = 0;
	const char *end = str + len;
	while (str < end) {
		if (*str == '\\') {
			if (++str == end) break;
			*newstr++ = *str++;
		} else {
			*newstr++ = *str++;
		}
	}
	*newstr = '';
	return i;
}

int remove_quote(char *newstr, const char *str, int len) {
	if (str[0] == '\"' && str[len - 1] == '\"') {
		/*
		strncpy(newstr, str + 1, len - 2)
		newstr[len - 2] = '';
		*/
		strcpy_unescape(newstr, str + 1, len - 2);
		LOG((debug, "quote removed=[true]\n"));
		return 1;
	} else {
		strcpy(newstr, str);
		LOG((debug, "quote removed=[false]\n"));
		return 0;
	}
}

int main(int argc, char *argv[]) {
	setlocale(LC_ALL, "");
	{
		int i;
		for (i = 0; i < argc; ++i) {
			LOG((debug, "argv[%d]=[%s]\n", i, argv[i]));
		}
	}
	if (argc != 3) return 127;
	if (strcmp(argv[1], "-c")) return 127;
	{
		int len = strlen(argv[2]);
		char *newstr = alloca(len + 1);
		remove_quote(newstr, argv[2], len);
		LOG((debug, "invoke=[%s]\n", newstr));
		return system(newstr);
	}
}

なんか今見ると、strcpy_unescape の戻り値 i の取り回しとか、\ のつぎが \ じゃなかったらどうなるのとか、
いろいろおかしい気がするが、気にしない。
[2010/08/23追記]上記をコンパイルしてfakesh.exeをつくったあと、以下の設定に変更する:

(setq shell-file-name "fakesh")

[2010/08/23追記おわり]
試行錯誤の結果、上記のようになったが、どういうことかというと、

  • ログにargvを出力させてみたところ、
    日本語が入っていないときは、argv[0]=[/usr/bin/fakesh]
    argv[1]=[-c]
    argv[2]=[lgrep -Ku8 -Ou8 -n ‘a’ {*,.*}]

    のようになっているが、日本語を入れると、

    argv[0]=[/usr/bin/fakesh]
    argv[1]=[-c]
    argv[2]=[“lgrep -Du8 -Ku8 -Ou8 –n ‘表\’ {*,.*}”]

    のように、””でクオートされた上に、\ が \\ に(クオートとか関係なく容赦なく)エスケープされてしまっている。
    (ただし、出力されている文字列はShift_JISではなくutf-8…。Shift_JISでクオートした上でutf-8になんかしているっぽい。)

  • なもので、日本語が入力されたときのargv[2]をそのままsystemに渡すと、
    “lgrep -Du8 -Ku8 -Ou8 –n ‘表\’ {*,.*}”
    を1つのコマンドとして実行しようとして、そんなコマンドはない、といわれてしまっていることになる。
  • ので ”” で囲まれた引数が来たときは、それを外してからsystemに渡すようにした。
    これで、とりあえず、動くようになった。
  • さらに、find-grepの場合、
    find . -type f -exec lgrep -Du8 -Ku8 -Ou8 -n ‘abc’ {} \;
    のように、;をクオートしてfindに渡す。
    ところが、日本語が入っていると、\ は \\ にクオートされてしまうので、
    ;のクオートがうまくいかないことになる。
    というわけで、””が囲まれていたときは、\\ を \ に戻す処理を入れたところ、find-grepも上手く動くようになった。

なお、fakesh.cは、cmd.exeではなく、cygwin上でビルドする必要がある。
メカニズムは追えていないが、cmd.exe上でビルドすると、utf-8ではなくShfit_JISで引数が渡ってくる。

lgrepが文字コードを推測してくれない問題に対処

ここまでで日本語を入れてもとりあえずgrepできるようになったが、まだ以下の問題が。

  • japanese-shift-jisのファイルとutf-8のファイル内の日本語文字列を検索すると、
    japanese-shift-jisのファイルしか検索してくれない。

もともとlgrepを使っているのは、文字コードを自動認識してくれるのを期待しているからなので、これは痛い。

いろいろ試行錯誤した結果、lgrepに-Du8をつけるとうまくいくことがわかった。

(setq grep-use-null-device nil)
(setq grep-command "lgrep -Du8 -Ku8 -Ou8 -n '' {*,.*}")
(setq grep-template "lgrep  -Du8 -Ku8 -Ou8 -n '' ")
(setq grep-find-command '("find . -type f -exec lgrep -Du8 -Ku8 -Ou8 -n '' {} \\;" . 47))
(defadvice grep (around grep-coding-setup activate)
  (let ((coding-system-for-read 'utf-8))
    ad-do-it))

ちなみに、lgrepをemacsからではなくコマンドラインから実行すると、逆にutf-8のファイルしか検索してくれない。
こちらについてはまだ解決策を見つけられていない…
とりあえず、-IsをつければShift_JISのファイル「だけ」を見てくれるようにはなるのだが…

findとlgrepを組み合わせて使ったときの問題に対処

lgrepの動作がバージョンアップ前と変わっているところは他にもある。

find-grepでfindとlgrepを組み合わせて使うと、ファイル名が表示されない。
結果、*grep*バッファから検索結果へジャンプできない。
これではgrepの意味が70%減。

これは、lgrepのほうをどうにかしないとどうしようもないので、lgrepにパッチをあてる。

cygwinのNetInstallerからソースを取得してビルドすればすぐできる、と思っていたが、
cygwin1.dllのビルドとは少々勝手が異なってちょっと手間取った。

まず、lv(lgrep)のcygwinパッケージは、cygbuildというものでつくられているらしい。
cygwin固有の差分を管理するためのしくみのよう。
で、このcygbuildはcygwinのNetInstallerでは配布されていないっぽい。なんてこと…。

とりあえず、http://www.nongnu.org/cygbuild/ を見てgitから入れることにする。

$ git clone git://git.savannah.nongnu.org/cygbuild.git
$ cd cygbuild
$ make install-symlink

何も考えるところはない。

次にcygbuildのしくみを使ってビルドする。
それには、NetInstaller から入手した lv-4.51-1-src.tar.bz2 を展開し、
そのまま、そこに含まれている lv-4.51-1.sh を実行する。

./lv-4.51-1.sh -v -c all

このとき、lv_4.51.orig.tar.gzを展開しておかないこと。展開されていると実行に失敗する。
途中、キーがらみのエラーを無視するか聞かれることがあるが、ここでは(危険性を認識した上で)無視して続行。

無事ビルドできたら、パッチを当ててもう一回ビルドを試みる。
パッチはこんな感じ。

diff -ur lv_4.51.orig/lv-4.51.orig/src/lv.c lv_4.51.orig-patched/lv-4.51.orig/src/lv.c
--- lv_4.51.orig/lv-4.51.orig/src/lv.c	Mon Jan 05 16:30:15 2004
+++ lv_4.51.orig-patched/lv-4.51.orig/src/lv.c	Mon Aug 23 01:51:16 2010
@@ -251,7 +251,7 @@
       /*
        * 対象ファイルが一個
        */
-      if( TRUE == LvGrep( conf, FALSE ) )
+      if( TRUE == LvGrep( conf, TRUE ) )
 	flagMatched = TRUE;
     } else {
       /*

で、lv_4.51.orig.tar.gzを展開したソースに当てた後、再度lv_4.51.orig.tar.gzに戻す。
なお、圧縮ファイルの取り扱いは、cygwin上で、tarを使ってやるのが望ましい。
Windows上のツールでやると、ビルド時に、configureのパーミッションエラーとか出るかもしれない。

無事ビルドできたら、できたlv-4.51-1.tar.bz2からバイナリを取り出してcygwinにインストールされているものと置き換える。

これで、findと組み合わせてもファイル名が表示されるようになった。万歳。

本当は…

emacs自体にパッチをあてて解決したいところ。

  • せっかくcygwinがutf-8に対応したのだから、emacsから渡す引数もutf-8に対応したい。
    もともと、NTEmacsでプロセスを開始するとき、A系のCreateProcessを(多分)使っていたので以前はあきらめていたが、
    utf-8であればあまり影響なく対応できるかも。
  • そもそも、fakeshのようなものが必要になるのはおかしいし。

でも、まだどういう理由でクオートやエスケープが入るのかメカニズムがわかっていない。

タスクマネージャの結果を信じるなら、emacsからCreateProcessするところまでは何もされておらず、
そこから、shのmain()に入るまでの間にクオートやエスケープが挿入されていることになる。
ということは、cygwinがなんかやっている?

謎は残っているものの、ひとまずはこの状態で様子を見てみることに。

——–

  • 2010/09/26
    LC_CTYPE を LC_TYPE にtypoしてた…
    LC_CTYPE=ja_JP.UTF-8 なら日本語がちゃんと通りました。というわけでfakeshは不要です。
広告

One Comment

Trackbacks

  1. NTEmacs で UTF-8 な環境構築を試行錯誤 | Amrta

現在コメントは受け付けていません。

%d人のブロガーが「いいね」をつけました。