zshをtcshなキーバインドで

ログインシェルとしてはもう十何年も tcsh をずっと使って来ていたのですが、All about Ruby on Rails & Data recovery software を素直に動作させるのが難しかったので zsh へ移行することを決意しました*1

【連載】漢のzsh | マイナビニュースを参考にしてだいたいは違和感なく移行できたのですが、最後までなかなか馴染まなかったのがキーバインディングでした。

最近ようやく tcsh とほぼ同じ感じになってきたので、どう設定したのかを以下にメモしておきます。

基本は Emacs バインディング

tcsh は基本的に emacs っぽいキーバインドなので、まずは全体的に emacs っぽくしました。

bindkey -e

M-p と M-n を変更

tcsh の M-p と M-n にはカーソルより左側が一致する履歴をどんどん表示する機能が割り当てられています。そこで zsh では以下のようにしました。

autoload history-search-end
zle -N history-beginning-search-backward-end history-search-end
zle -N history-beginning-search-forward-end history-search-end
bindkey "^[p" history-beginning-search-backward-end
bindkey "^[n" history-beginning-search-forward-end

M-f や M-b などの単語を扱う機能の調節

この機能が一番のくせ者でした。結論から言えば、以下を ~/.zshrc に書き、

autoload -Uz select-word-style
select-word-style default
zstyle ':zle:*' word-chars '*?_-.[]~='

fpath=( ~/.home/zsh $fpath )
autoload -Uz tcsh-forward-word-match
zle -N forward-word tcsh-forward-word-match

以下を ~/.home/zsh/tcsh-forward-word-match に書きました。

# modified from /usr/share/zsh/4.3.12/functions/forward-word-match
emulate -L zsh
setopt extendedglob

autoload -Uz match-words-by-style

local curcontext=":zle:$WIDGET" word
local -a matched_words
integer count=${NUMERIC:-1}

if (( count < 0 )); then
    (( NUMERIC = -count ))
    zle ${WIDGET/forward/backward}
    return
fi

while (( count-- )); do

    match-words-by-style

    # For some reason forward-word doesn't work like the other word
    # commands; it skips whitespace only after any matched word
    # characters.

#    if [[ -n $matched_words[4] ]]; then
#        # just skip the whitespace
#	word=$matched_words[4]
#    else
#        # skip the word and trailing whitespace
#	word=$matched_words[5]$matched_words[6]
#    fi
    if [[ -n $matched_words[4] ]]; then
        # skip the whitespace and the word
	word=$matched_words[4]$matched_words[5]
    else
        # jist skip the word
	word=$matched_words[5]
    fi

    if [[ -n $word ]]; then
	(( CURSOR += ${#word} ))
    else
	return 1
    fi
done

return 0

M-f M-b の各シェルの動作

上記の設定の意味の解説をする前に、まずは tcsh, bash, zsh での動作を確認しておきます。(以下ではカーソル位置はスタイルシートの background-color で表現してありますので、RSS リーダーでご覧の場合には見えないかもしれません)

tcsh の場合 M-f を押すたびに次のようにカーソル(赤い四角)が移動します。

% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin 

逆に M-b を押すたびに次のようにカーソルが移動します。

% ls -l /usr/bin 
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin

M-f と M-b では停止位置が微妙に違うところがポイントです。

bash の場合 M-f の動作は tcsh と全く同じです。

$ ls -l /usr/bin
$ ls -l /usr/bin
$ ls -l /usr/bin
$ ls -l /usr/bin
$ ls -l /usr/bin 

しかし M-b の動作は -l の箇所の停止位置(青い四角)が tcsh とは異なります。

$ ls -l /usr/bin 
$ ls -l /usr/bin
$ ls -l /usr/bin
$ ls -l /usr/bin
$ ls -l /usr/bin

zsh を select-word-style bash で動作させた場合は、M-f の動作は次のようになります。

% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin 

bash の動作とは全く異なります。逆に M-b を押した場合は次のようになり bash の動作と一致します。

% ls -l /usr/bin 
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin

また、M-f での停止位置と M-b の停止位置が一致しています。

zsh を select-word-style default で動作させた場合は、M-f の動作は次のようになります。

% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin 

各引数の最初の文字に停止します。M-b を押した時も停止位置は同じです。

% ls -l /usr/bin 
% ls -l /usr/bin
% ls -l /usr/bin
% ls -l /usr/bin

M-f M-b 解説

コマンドラインを構成する文字は単語を構成する文字(w とする)と単語を構成しない文字(. とする)の2種類にわけることができます。

すると tcshbash の場合のカーソルが停止する位置は、

  • M-f の場合はカーソルより右側の w. の境目 (www...)
  • M-b の場合はカーソルより左側の .w の境目 (...www)

となります。

zsh の場合は、

  • M-f の場合はカーソルより右側の .w の境目 (...www)
  • M-b の場合は tcsh, bash と同じ

となります。

そして単語を構成する文字は以下のようになっています。

tcsh [:alnum:] と '*?_-.[]~='
bash [:alnum:]*2
zsh で select-word-style bash [:alnum:]
zsh で select-word-style default [:alnum:] と '*?_-.[]~=/&;!#$%^(){}<>'

そこで、まずは zsh の単語を構成する文字を tcsh と一致させるために次のような設定をしました。

autoload -Uz select-word-style
select-word-style default
zstyle ':zle:*' word-chars '*?_-.[]~='

select-word-style default 時は word-chars に設定した文字に加えて [:alnum:] が単語を構成する文字になりますので、これで tcsh と一致します。

そして M-f の時の動作を変更するために次のような設定をし、

fpath=( ~/.home/zsh $fpath )
autoload -Uz tcsh-forward-word-match
zle -N forward-word tcsh-forward-word-match

~/.home/zsh/tcsh-forward-word-match を作成しました。tcsh-forward-word-match はカーソルより右側の w. の境目 (www...) へカーソルを移動させる関数です。

これでようやく tcsh とほぼ同じ感じで動作するようになりました。

*1:C Shell 系ではなく Bourne Shell 系のシェルを使いたいとずっと思っていたというのもあります

*2:未確認ですがおそらくこうなっています

Objective-C の __block の参照カウンタを調査中…

と呟いたところ、本の著者さんからというお返事をいただいたので、 splhack: 『iOS 4プログラミングブック』 第5章マルチスレッド 補遺 その2 の例を自分でも確かめてみました。

という設定で上記の @ さんの記事のコード(以下に再掲)をビルドし、

#import <Foundation/Foundation.h>
#import <stdio.h>

extern const char *_Block_byref_dump(void *);

void dump(int line, int *p)
{
    p -= 4;
    printf("\ndump line:%d\n", line);
    puts(_Block_byref_dump(p));
}

int *test()
{
    __block int total = 11;
    
    dump(__LINE__, &total);
    
    void (^block_on_stack)() = ^{
        
        ++total;
        
        dump(__LINE__, &total);
    };
    
    block_on_stack();
    
    printf("\n___ Block_copy ___\n");
    void (^block_on_heap)() = Block_copy(block_on_stack);
    
    dump(__LINE__, &total);
    
    block_on_stack();
    
    block_on_heap();
    
    printf("\n___ Block_release ___\n");
    Block_release(block_on_heap);
    
    dump(__LINE__, &total);
    
    block_on_stack();
    
    return &total;
}

int main()
{
    dump(__LINE__, test());
}

Assembly を見ると、test() の最後で以下のようなコードが実行されているのが見つかります。

	.loc	1 45 1                  ## (略)/main.m:45:1
	movl	-112(%ebp), %eax        ## 4-byte Reload
	movl	%eax, (%esp)
	movl	$8, 4(%esp)
	calll	__Block_object_dispose

どうやら __block 変数の __forwarding 先の参照カウントは、Block_copy() した時にスタックからの参照分とヒープから参照分だけカウンタを増やしておいて、Block_release() 時とスコープの終わりに挿入される _Block_object_dispose() 時に減らしているようですね。

https://llvm.org/svn/llvm-project/compiler-rt/trunk/BlocksRuntime/runtime.c

void _Block_object_dispose(const void *object, const int flags) {
    // (略)
    if (flags & BLOCK_FIELD_IS_BYREF)  {
        // get rid of the __block data structure held in a Block
        _Block_byref_release(object);
    }
    // (略)
}

というわけで

という心配は杞憂のようでした。

ところで、実行してみたところ次のような出力を得ました。

dump line:17
byref data block 0xbffff9c0 contents:
  forwarding: 0xbffff9c0
  flags: 0x0
  size: 20


dump line:23
byref data block 0xbffff9c0 contents:
  forwarding: 0xbffff9c0
  flags: 0x0
  size: 20


___ Block_copy ___

dump line:31
byref data block 0x13e450 contents:
  forwarding: 0x13e450
  flags: 0x1000004
  size: 20


dump line:23
byref data block 0x13e450 contents:
  forwarding: 0x13e450
  flags: 0x1000004
  size: 20


dump line:23
byref data block 0x13e450 contents:
  forwarding: 0x13e450
  flags: 0x1000004
  size: 20


___ Block_release ___

dump line:40
byref data block 0x13e450 contents:
  forwarding: 0x13e450
  flags: 0x1000002
  size: 20


dump line:23
byref data block 0x13e450 contents:
  forwarding: 0x13e450
  flags: 0x1000002
  size: 20


dump line:49
byref data block 0x13e450 contents:
  forwarding: 0x13e450
  flags: 0x1000001
  size: 20

@ さんの記事の結果とは以下の点が若干異なっています。

  • 謎1: flags の下位 16bit の参照カウントが、Block_copy() で 4 増え、Block_release() で 2 減る
    • _Block_object_dispose() 内では flags を 1 しか減らしていないように見えるのですが…
  • 謎2: 最後の dump line:49 では参照カウントが 1 のまま
    • 参照カウントは OSAtomicCompareAndSwapInt() で減らされるので、最後はちゃんと 0 になるべきであるはず


というお返事をいただいたので libclosure-53 を覗いてみると、

http://www.opensource.apple.com/source/libclosure/libclosure-53/Block_private.h

enum {
    BLOCK_DEALLOCATING =      (0x0001),
    BLOCK_REFCOUNT_MASK =     (0xfffe),
    BLOCK_NEEDS_FREE =        (1 << 24),
    (略)
};

となっており、参照カウントは1bit左へシフトされていました。上記の結果になるのも納得です。

win-ssh-agent 1.07

win-ssh-agent を使用すると、cygwin の openssh の ssh-agent をよりスマートに利用できるようになります。

通常 ssh-agent を利用するためには、ssh-agent を起動したシェル (例: bash) からその他のプログラムを起動する必要があります。これは、ssh-agent が設定する環境変数 SSH_AUTH_SOCK を他のソフトウェアから参照できなければならないためです。

win-ssh-agent は、ssh-agent が設定する環境変数を全てのプログラムが参照できるようにしますので、特定のシェルから他のプログラムを起動する必要がなくなります。

詳しくは README-ja.txt (README.txt)を参照してください。

ダウンロード:

2011/11/02 1.07:

  • プロジェクト名を win-ssh-askpass から win-ssh-agent へ変更。
  • --hide-console オプションを削除。指定しても無視されます。
  • win-ssh-agent.exe をダブルクリックして起動したときに、コンソールウィンドウを開かないように変更。
  • openssh 5.9p1-1 のバグを回避するために、内部構造を変更
  • README の修正
  • 細かな修正

win-ssh-askpass 1.06

(2011-11-02 追記) 最新版はこちら ⇒ win-ssh-agent 1.07 - GANAwareはてな版

win-ssh-agent は X 用の ssh-askpass と同様の機能を提供します。詳しくは README-ja.txt (README.txt)を参照してください。

ダウンロード:

2011/10/14 1.06:

  • cygwin 1.7 用の修正
    • 最新の cygwin 環境でビルドできなくなっていたので修正しました
    • ダイアログボックスの見た目が Windows7 っぽくなりました
    • Unicode API を積極的に使うようにしました

メモ: Lion で Firefox をビルド

Building Firefox for macOS - Mozilla | MDN を参考に:

(1) App StoreXcode を入れる

(2) homebrew を入れる

(3) Mercurial を入れる

$ brew install Mercurial
Error: No available formula for Mercurial
Install Mercurial with pip:

    easy_install pip && pip install Mercurial

Or easy_install:

    easy_install Mercurial
$ sudo easy_install pip
$ sudo pip install Mercurial

(4) 他に必要な物を homebrew で入れる

$ brew install pkg-config
$ brew install libIDL

(5) autoconf213 を homebrew で入れる

  • その前に Lonnen から autoconf213.rb をダウンロードして /usr/local/Library/Formula/autoconf213.rb へ置いておく
$ brew install autoconf213

(6) ソースを入手

$ hg clone http://hg.mozilla.org/mozilla-central/ mozilla

(7) .mozconfig を作成

  • webm と libjpeg-turbo はとりあえず使用しないことにした
$ cat > mozilla/.mozconfig
. $topsrcdir/browser/config/mozconfig

mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-ff-dbg
mk_add_options MOZ_MAKE_FLAGS="-s -j4"
ac_add_options --enable-debug
ac_add_options --disable-optimize
ac_add_options --disable-webm
ac_add_options --disable-libjpeg-turbo

mk_add_options AUTOCONF=/usr/local/Cellar/autoconf213/2.13/bin/autoconf213

(8) ビルド

$ cd mozilla
$ make -f client.mk build

(9) 実行

$ cd mozilla
$ open obj-ff-dbg/dist/NightlyDebug.app

Win32 Subversion 1.5.0 からは APR-iconv は不要

Win32 Subversion は 1.5.0 以降は APR-iconv を必要としないので、APR_ICONV_PATH を設定する必要はありません。バイナリパッケージをダウンロードする時に Win32Svn (32-bit client, server and bindings, MSI and ZIPs; maintained by David Darj) を選択しインストールした場合 APR_ICONV_PATH が環境変数に設定されますが、これは不要です(メールフォームからフィードバックしておきました)。

Win32 Subversion が APR-iconv に依存しないようにしようという話は "SVN Win32 Developers -- need some help""[PATCH] Remove APR ICONV dependency on Windows" あたりのスレッドで議論され、r865724 で対策コードが commit されました。

ということで、対策コードは 1.5.0 から含まれるようになりました。



(2011-09-14 追記) David Darj から返事が来て、1.7.0 のリリースからは APR-iconv を含めないようにするとのことです。

はてなブックマーク / みたくないリンク (Google Chrome 拡張版) ver 0.3

はてなブックマーク/見たくないリンク - Chrome Web Store

私は複数マシンで Chrome を使用しているのですが、それぞれでURL情報を共有するのは結構めんどくさいものでした。

そこでエロサイトのURL情報を wedata で管理し、そこから定期的にURL情報をロードするように変更しました。


ver 0.3 から新たに拡張が外部サイトへアクセスするようになるので、0.2 から 0.3 へ自動でアップデートした場合は拡張が一時的に無効状態になるようです。Chrome の拡張は最初にインストールした時より拡張の能力が増える場合は自動的に無効になり、ユーザーに条件を確認させるようになっているのですね。